LCOV - code coverage report
Current view: top level - pengine - pe_actions.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 740 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 40 0.0 %

          Line data    Source code
       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 <glib.h>
      13             : #include <stdbool.h>
      14             : 
      15             : #include <crm/crm.h>
      16             : #include <crm/common/xml.h>
      17             : #include <crm/common/scheduler_internal.h>
      18             : #include <crm/pengine/internal.h>
      19             : #include <crm/common/xml_internal.h>
      20             : #include "pe_status_private.h"
      21             : 
      22             : static void unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
      23             :                              guint interval_ms);
      24             : 
      25             : static void
      26           0 : add_singleton(pcmk_scheduler_t *scheduler, pcmk_action_t *action)
      27             : {
      28           0 :     if (scheduler->singletons == NULL) {
      29           0 :         scheduler->singletons = pcmk__strkey_table(NULL, NULL);
      30             :     }
      31           0 :     g_hash_table_insert(scheduler->singletons, action->uuid, action);
      32           0 : }
      33             : 
      34             : static pcmk_action_t *
      35           0 : lookup_singleton(pcmk_scheduler_t *scheduler, const char *action_uuid)
      36             : {
      37           0 :     if (scheduler->singletons == NULL) {
      38           0 :         return NULL;
      39             :     }
      40           0 :     return g_hash_table_lookup(scheduler->singletons, action_uuid);
      41             : }
      42             : 
      43             : /*!
      44             :  * \internal
      45             :  * \brief Find an existing action that matches arguments
      46             :  *
      47             :  * \param[in] key        Action key to match
      48             :  * \param[in] rsc        Resource to match (if any)
      49             :  * \param[in] node       Node to match (if any)
      50             :  * \param[in] scheduler  Scheduler data
      51             :  *
      52             :  * \return Existing action that matches arguments (or NULL if none)
      53             :  */
      54             : static pcmk_action_t *
      55           0 : find_existing_action(const char *key, const pcmk_resource_t *rsc,
      56             :                      const pcmk_node_t *node, const pcmk_scheduler_t *scheduler)
      57             : {
      58           0 :     GList *matches = NULL;
      59           0 :     pcmk_action_t *action = NULL;
      60             : 
      61             :     /* When rsc is NULL, it would be quicker to check scheduler->singletons,
      62             :      * but checking all scheduler->actions takes the node into account.
      63             :      */
      64           0 :     matches = find_actions(((rsc == NULL)? scheduler->actions : rsc->actions),
      65             :                            key, node);
      66           0 :     if (matches == NULL) {
      67           0 :         return NULL;
      68             :     }
      69           0 :     CRM_LOG_ASSERT(!pcmk__list_of_multiple(matches));
      70             : 
      71           0 :     action = matches->data;
      72           0 :     g_list_free(matches);
      73           0 :     return action;
      74             : }
      75             : 
      76             : /*!
      77             :  * \internal
      78             :  * \brief Find the XML configuration corresponding to a specific action key
      79             :  *
      80             :  * \param[in] rsc               Resource to find action configuration for
      81             :  * \param[in] key               "RSC_ACTION_INTERVAL" of action to find
      82             :  * \param[in] include_disabled  If false, do not return disabled actions
      83             :  *
      84             :  * \return XML configuration of desired action if any, otherwise NULL
      85             :  */
      86             : static xmlNode *
      87           0 : find_exact_action_config(const pcmk_resource_t *rsc, const char *action_name,
      88             :                          guint interval_ms, bool include_disabled)
      89             : {
      90           0 :     for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
      91             :                                                    NULL, NULL);
      92           0 :          operation != NULL; operation = pcmk__xe_next_same(operation)) {
      93             : 
      94           0 :         bool enabled = false;
      95           0 :         const char *config_name = NULL;
      96           0 :         const char *interval_spec = NULL;
      97           0 :         guint tmp_ms = 0U;
      98             : 
      99             :         // @TODO This does not consider meta-attributes, rules, defaults, etc.
     100           0 :         if (!include_disabled
     101           0 :             && (pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED,
     102           0 :                                        &enabled) == pcmk_rc_ok) && !enabled) {
     103           0 :             continue;
     104             :         }
     105             : 
     106           0 :         interval_spec = crm_element_value(operation, PCMK_META_INTERVAL);
     107           0 :         pcmk_parse_interval_spec(interval_spec, &tmp_ms);
     108           0 :         if (tmp_ms != interval_ms) {
     109           0 :             continue;
     110             :         }
     111             : 
     112           0 :         config_name = crm_element_value(operation, PCMK_XA_NAME);
     113           0 :         if (pcmk__str_eq(action_name, config_name, pcmk__str_none)) {
     114           0 :             return operation;
     115             :         }
     116             :     }
     117           0 :     return NULL;
     118             : }
     119             : 
     120             : /*!
     121             :  * \internal
     122             :  * \brief Find the XML configuration of a resource action
     123             :  *
     124             :  * \param[in] rsc               Resource to find action configuration for
     125             :  * \param[in] action_name       Action name to search for
     126             :  * \param[in] interval_ms       Action interval (in milliseconds) to search for
     127             :  * \param[in] include_disabled  If false, do not return disabled actions
     128             :  *
     129             :  * \return XML configuration of desired action if any, otherwise NULL
     130             :  */
     131             : xmlNode *
     132           0 : pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name,
     133             :                          guint interval_ms, bool include_disabled)
     134             : {
     135           0 :     xmlNode *action_config = NULL;
     136             : 
     137             :     // Try requested action first
     138           0 :     action_config = find_exact_action_config(rsc, action_name, interval_ms,
     139             :                                              include_disabled);
     140             : 
     141             :     // For migrate_to and migrate_from actions, retry with "migrate"
     142             :     // @TODO This should be either documented or deprecated
     143           0 :     if ((action_config == NULL)
     144           0 :         && pcmk__str_any_of(action_name, PCMK_ACTION_MIGRATE_TO,
     145             :                             PCMK_ACTION_MIGRATE_FROM, NULL)) {
     146           0 :         action_config = find_exact_action_config(rsc, "migrate", 0,
     147             :                                                  include_disabled);
     148             :     }
     149             : 
     150           0 :     return action_config;
     151             : }
     152             : 
     153             : /*!
     154             :  * \internal
     155             :  * \brief Create a new action object
     156             :  *
     157             :  * \param[in]     key        Action key
     158             :  * \param[in]     task       Action name
     159             :  * \param[in,out] rsc        Resource that action is for (if any)
     160             :  * \param[in]     node       Node that action is on (if any)
     161             :  * \param[in]     optional   Whether action should be considered optional
     162             :  * \param[in,out] scheduler  Scheduler data
     163             :  *
     164             :  * \return Newly allocated action
     165             :  * \note This function takes ownership of \p key. It is the caller's
     166             :  *       responsibility to free the return value with pe_free_action().
     167             :  */
     168             : static pcmk_action_t *
     169           0 : new_action(char *key, const char *task, pcmk_resource_t *rsc,
     170             :            const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
     171             : {
     172           0 :     pcmk_action_t *action = pcmk__assert_alloc(1, sizeof(pcmk_action_t));
     173             : 
     174           0 :     action->rsc = rsc;
     175           0 :     action->task = pcmk__str_copy(task);
     176           0 :     action->uuid = key;
     177             : 
     178           0 :     if (node) {
     179           0 :         action->node = pe__copy_node(node);
     180             :     }
     181             : 
     182           0 :     if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_casei)) {
     183             :         // Resource history deletion for a node can be done on the DC
     184           0 :         pcmk__set_action_flags(action, pcmk_action_on_dc);
     185             :     }
     186             : 
     187           0 :     pcmk__set_action_flags(action, pcmk_action_runnable);
     188           0 :     if (optional) {
     189           0 :         pcmk__set_action_flags(action, pcmk_action_optional);
     190             :     } else {
     191           0 :         pcmk__clear_action_flags(action, pcmk_action_optional);
     192             :     }
     193             : 
     194           0 :     if (rsc == NULL) {
     195           0 :         action->meta = pcmk__strkey_table(free, free);
     196             :     } else {
     197           0 :         guint interval_ms = 0;
     198             : 
     199           0 :         parse_op_key(key, NULL, NULL, &interval_ms);
     200           0 :         action->op_entry = pcmk__find_action_config(rsc, task, interval_ms,
     201             :                                                     true);
     202             : 
     203             :         /* If the given key is for one of the many notification pseudo-actions
     204             :          * (pre_notify_promote, etc.), the actual action name is "notify"
     205             :          */
     206           0 :         if ((action->op_entry == NULL) && (strstr(key, "_notify_") != NULL)) {
     207           0 :             action->op_entry = find_exact_action_config(rsc, PCMK_ACTION_NOTIFY,
     208             :                                                         0, true);
     209             :         }
     210             : 
     211           0 :         unpack_operation(action, action->op_entry, interval_ms);
     212             :     }
     213             : 
     214           0 :     pcmk__rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
     215             :                     (optional? "optional" : "required"),
     216             :                     scheduler->action_id, key, task,
     217             :                     ((rsc == NULL)? "no resource" : rsc->id),
     218             :                     pcmk__node_name(node));
     219           0 :     action->id = scheduler->action_id++;
     220             : 
     221           0 :     scheduler->actions = g_list_prepend(scheduler->actions, action);
     222           0 :     if (rsc == NULL) {
     223           0 :         add_singleton(scheduler, action);
     224             :     } else {
     225           0 :         rsc->actions = g_list_prepend(rsc->actions, action);
     226             :     }
     227           0 :     return action;
     228             : }
     229             : 
     230             : /*!
     231             :  * \internal
     232             :  * \brief Unpack a resource's action-specific instance parameters
     233             :  *
     234             :  * \param[in]     action_xml  XML of action's configuration in CIB (if any)
     235             :  * \param[in,out] node_attrs  Table of node attributes (for rule evaluation)
     236             :  * \param[in,out] scheduler   Cluster working set (for rule evaluation)
     237             :  *
     238             :  * \return Newly allocated hash table of action-specific instance parameters
     239             :  */
     240             : GHashTable *
     241           0 : pcmk__unpack_action_rsc_params(const xmlNode *action_xml,
     242             :                                GHashTable *node_attrs,
     243             :                                pcmk_scheduler_t *scheduler)
     244             : {
     245           0 :     GHashTable *params = pcmk__strkey_table(free, free);
     246             : 
     247           0 :     pe_rule_eval_data_t rule_data = {
     248             :         .node_hash = node_attrs,
     249           0 :         .now = scheduler->now,
     250             :         .match_data = NULL,
     251             :         .rsc_data = NULL,
     252             :         .op_data = NULL
     253             :     };
     254             : 
     255           0 :     pe__unpack_dataset_nvpairs(action_xml, PCMK_XE_INSTANCE_ATTRIBUTES,
     256             :                                &rule_data, params, NULL,
     257             :                                FALSE, scheduler);
     258           0 :     return params;
     259             : }
     260             : 
     261             : /*!
     262             :  * \internal
     263             :  * \brief Update an action's optional flag
     264             :  *
     265             :  * \param[in,out] action    Action to update
     266             :  * \param[in]     optional  Requested optional status
     267             :  */
     268             : static void
     269           0 : update_action_optional(pcmk_action_t *action, gboolean optional)
     270             : {
     271             :     // Force a non-recurring action to be optional if its resource is unmanaged
     272           0 :     if ((action->rsc != NULL) && (action->node != NULL)
     273           0 :         && !pcmk_is_set(action->flags, pcmk_action_pseudo)
     274           0 :         && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
     275           0 :         && (g_hash_table_lookup(action->meta, PCMK_META_INTERVAL) == NULL)) {
     276           0 :             pcmk__rsc_debug(action->rsc,
     277             :                             "%s on %s is optional (%s is unmanaged)",
     278             :                             action->uuid, pcmk__node_name(action->node),
     279             :                             action->rsc->id);
     280           0 :             pcmk__set_action_flags(action, pcmk_action_optional);
     281             :             // We shouldn't clear runnable here because ... something
     282             : 
     283             :     // Otherwise require the action if requested
     284           0 :     } else if (!optional) {
     285           0 :         pcmk__clear_action_flags(action, pcmk_action_optional);
     286             :     }
     287           0 : }
     288             : 
     289             : static enum pe_quorum_policy
     290           0 : effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
     291             : {
     292           0 :     enum pe_quorum_policy policy = scheduler->no_quorum_policy;
     293             : 
     294           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_quorate)) {
     295           0 :         policy = pcmk_no_quorum_ignore;
     296             : 
     297           0 :     } else if (scheduler->no_quorum_policy == pcmk_no_quorum_demote) {
     298           0 :         switch (rsc->role) {
     299           0 :             case pcmk_role_promoted:
     300             :             case pcmk_role_unpromoted:
     301           0 :                 if (rsc->next_role > pcmk_role_unpromoted) {
     302           0 :                     pe__set_next_role(rsc, pcmk_role_unpromoted,
     303             :                                       PCMK_OPT_NO_QUORUM_POLICY "=demote");
     304             :                 }
     305           0 :                 policy = pcmk_no_quorum_ignore;
     306           0 :                 break;
     307           0 :             default:
     308           0 :                 policy = pcmk_no_quorum_stop;
     309           0 :                 break;
     310             :         }
     311             :     }
     312           0 :     return policy;
     313             : }
     314             : 
     315             : /*!
     316             :  * \internal
     317             :  * \brief Update a resource action's runnable flag
     318             :  *
     319             :  * \param[in,out] action     Action to update
     320             :  * \param[in,out] scheduler  Scheduler data
     321             :  *
     322             :  * \note This may also schedule fencing if a stop is unrunnable.
     323             :  */
     324             : static void
     325           0 : update_resource_action_runnable(pcmk_action_t *action,
     326             :                                 pcmk_scheduler_t *scheduler)
     327             : {
     328           0 :     if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
     329           0 :         return;
     330             :     }
     331             : 
     332           0 :     if (action->node == NULL) {
     333           0 :         pcmk__rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
     334             :                         action->uuid);
     335           0 :         pcmk__clear_action_flags(action, pcmk_action_runnable);
     336             : 
     337           0 :     } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc)
     338           0 :                && !(action->node->details->online)
     339           0 :                && (!pcmk__is_guest_or_bundle_node(action->node)
     340           0 :                    || action->node->details->remote_requires_reset)) {
     341           0 :         pcmk__clear_action_flags(action, pcmk_action_runnable);
     342           0 :         do_crm_log(LOG_WARNING, "%s on %s is unrunnable (node is offline)",
     343             :                    action->uuid, pcmk__node_name(action->node));
     344           0 :         if (pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
     345           0 :             && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)
     346           0 :             && !(action->node->details->unclean)) {
     347           0 :             pe_fence_node(scheduler, action->node, "stop is unrunnable", false);
     348             :         }
     349             : 
     350           0 :     } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc)
     351           0 :                && action->node->details->pending) {
     352           0 :         pcmk__clear_action_flags(action, pcmk_action_runnable);
     353           0 :         do_crm_log(LOG_WARNING,
     354             :                    "Action %s on %s is unrunnable (node is pending)",
     355             :                    action->uuid, pcmk__node_name(action->node));
     356             : 
     357           0 :     } else if (action->needs == pcmk_requires_nothing) {
     358           0 :         pe_action_set_reason(action, NULL, TRUE);
     359           0 :         if (pcmk__is_guest_or_bundle_node(action->node)
     360           0 :             && !pe_can_fence(scheduler, action->node)) {
     361             :             /* An action that requires nothing usually does not require any
     362             :              * fencing in order to be runnable. However, there is an exception:
     363             :              * such an action cannot be completed if it is on a guest node whose
     364             :              * host is unclean and cannot be fenced.
     365             :              */
     366           0 :             pcmk__rsc_debug(action->rsc,
     367             :                             "%s on %s is unrunnable "
     368             :                             "(node's host cannot be fenced)",
     369             :                             action->uuid, pcmk__node_name(action->node));
     370           0 :             pcmk__clear_action_flags(action, pcmk_action_runnable);
     371             :         } else {
     372           0 :             pcmk__rsc_trace(action->rsc,
     373             :                             "%s on %s does not require fencing or quorum",
     374             :                             action->uuid, pcmk__node_name(action->node));
     375           0 :             pcmk__set_action_flags(action, pcmk_action_runnable);
     376             :         }
     377             : 
     378             :     } else {
     379           0 :         switch (effective_quorum_policy(action->rsc, scheduler)) {
     380           0 :             case pcmk_no_quorum_stop:
     381           0 :                 pcmk__rsc_debug(action->rsc,
     382             :                                 "%s on %s is unrunnable (no quorum)",
     383             :                                 action->uuid, pcmk__node_name(action->node));
     384           0 :                 pcmk__clear_action_flags(action, pcmk_action_runnable);
     385           0 :                 pe_action_set_reason(action, "no quorum", true);
     386           0 :                 break;
     387             : 
     388           0 :             case pcmk_no_quorum_freeze:
     389           0 :                 if (!action->rsc->fns->active(action->rsc, TRUE)
     390           0 :                     || (action->rsc->next_role > action->rsc->role)) {
     391           0 :                     pcmk__rsc_debug(action->rsc,
     392             :                                     "%s on %s is unrunnable (no quorum)",
     393             :                                     action->uuid,
     394             :                                     pcmk__node_name(action->node));
     395           0 :                     pcmk__clear_action_flags(action, pcmk_action_runnable);
     396           0 :                     pe_action_set_reason(action, "quorum freeze", true);
     397             :                 }
     398           0 :                 break;
     399             : 
     400           0 :             default:
     401             :                 //pe_action_set_reason(action, NULL, TRUE);
     402           0 :                 pcmk__set_action_flags(action, pcmk_action_runnable);
     403           0 :                 break;
     404             :         }
     405             :     }
     406             : }
     407             : 
     408             : /*!
     409             :  * \internal
     410             :  * \brief Update a resource object's flags for a new action on it
     411             :  *
     412             :  * \param[in,out] rsc     Resource that action is for (if any)
     413             :  * \param[in]     action  New action
     414             :  */
     415             : static void
     416           0 : update_resource_flags_for_action(pcmk_resource_t *rsc,
     417             :                                  const pcmk_action_t *action)
     418             : {
     419             :     /* @COMPAT pcmk_rsc_starting and pcmk_rsc_stopping are deprecated and unused
     420             :      * within Pacemaker, and will eventually be removed
     421             :      */
     422           0 :     if (pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)) {
     423           0 :         pcmk__set_rsc_flags(rsc, pcmk_rsc_stopping);
     424             : 
     425           0 :     } else if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_casei)) {
     426           0 :         if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
     427           0 :             pcmk__set_rsc_flags(rsc, pcmk_rsc_starting);
     428             :         } else {
     429           0 :             pcmk__clear_rsc_flags(rsc, pcmk_rsc_starting);
     430             :         }
     431             :     }
     432           0 : }
     433             : 
     434             : static bool
     435           0 : valid_stop_on_fail(const char *value)
     436             : {
     437           0 :     return !pcmk__strcase_any_of(value,
     438             :                                  PCMK_VALUE_STANDBY, PCMK_VALUE_DEMOTE,
     439           0 :                                  PCMK_VALUE_STOP, NULL);
     440             : }
     441             : 
     442             : /*!
     443             :  * \internal
     444             :  * \brief Validate (and possibly reset) resource action's on_fail meta-attribute
     445             :  *
     446             :  * \param[in]     rsc            Resource that action is for
     447             :  * \param[in]     action_name    Action name
     448             :  * \param[in]     action_config  Action configuration XML from CIB (if any)
     449             :  * \param[in,out] meta           Table of action meta-attributes
     450             :  */
     451             : static void
     452           0 : validate_on_fail(const pcmk_resource_t *rsc, const char *action_name,
     453             :                  const xmlNode *action_config, GHashTable *meta)
     454             : {
     455           0 :     const char *name = NULL;
     456           0 :     const char *role = NULL;
     457           0 :     const char *interval_spec = NULL;
     458           0 :     const char *value = g_hash_table_lookup(meta, PCMK_META_ON_FAIL);
     459           0 :     guint interval_ms = 0U;
     460             : 
     461             :     // Stop actions can only use certain on-fail values
     462           0 :     if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)
     463           0 :         && !valid_stop_on_fail(value)) {
     464             : 
     465           0 :         pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s stop "
     466             :                          "action to default value because '%s' is not "
     467             :                          "allowed for stop", rsc->id, value);
     468           0 :         g_hash_table_remove(meta, PCMK_META_ON_FAIL);
     469           0 :         return;
     470             :     }
     471             : 
     472             :     /* Demote actions default on-fail to the on-fail value for the first
     473             :      * recurring monitor for the promoted role (if any).
     474             :      */
     475           0 :     if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)
     476           0 :         && (value == NULL)) {
     477             : 
     478             :         /* @TODO This does not consider promote options set in a meta-attribute
     479             :          * block (which may have rules that need to be evaluated) rather than
     480             :          * XML properties.
     481             :          */
     482           0 :         for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
     483             :                                                        NULL, NULL);
     484           0 :              operation != NULL; operation = pcmk__xe_next_same(operation)) {
     485             : 
     486           0 :             bool enabled = false;
     487           0 :             const char *promote_on_fail = NULL;
     488             : 
     489             :             /* We only care about explicit on-fail (if promote uses default, so
     490             :              * can demote)
     491             :              */
     492           0 :             promote_on_fail = crm_element_value(operation, PCMK_META_ON_FAIL);
     493           0 :             if (promote_on_fail == NULL) {
     494           0 :                 continue;
     495             :             }
     496             : 
     497             :             // We only care about recurring monitors for the promoted role
     498           0 :             name = crm_element_value(operation, PCMK_XA_NAME);
     499           0 :             role = crm_element_value(operation, PCMK_XA_ROLE);
     500           0 :             if (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
     501           0 :                 || !pcmk__strcase_any_of(role, PCMK_ROLE_PROMOTED,
     502             :                                          PCMK__ROLE_PROMOTED_LEGACY, NULL)) {
     503           0 :                 continue;
     504             :             }
     505           0 :             interval_spec = crm_element_value(operation, PCMK_META_INTERVAL);
     506           0 :             pcmk_parse_interval_spec(interval_spec, &interval_ms);
     507           0 :             if (interval_ms == 0U) {
     508           0 :                 continue;
     509             :             }
     510             : 
     511             :             // We only care about enabled monitors
     512           0 :             if ((pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED,
     513           0 :                                         &enabled) == pcmk_rc_ok) && !enabled) {
     514           0 :                 continue;
     515             :             }
     516             : 
     517             :             /* Demote actions can't default to
     518             :              * PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE
     519             :              */
     520           0 :             if (pcmk__str_eq(promote_on_fail, PCMK_VALUE_DEMOTE,
     521             :                              pcmk__str_casei)) {
     522           0 :                 continue;
     523             :             }
     524             : 
     525             :             // Use value from first applicable promote action found
     526           0 :             pcmk__insert_dup(meta, PCMK_META_ON_FAIL, promote_on_fail);
     527             :         }
     528           0 :         return;
     529             :     }
     530             : 
     531           0 :     if (pcmk__str_eq(action_name, PCMK_ACTION_LRM_DELETE, pcmk__str_none)
     532           0 :         && !pcmk__str_eq(value, PCMK_VALUE_IGNORE, pcmk__str_casei)) {
     533             : 
     534           0 :         pcmk__insert_dup(meta, PCMK_META_ON_FAIL, PCMK_VALUE_IGNORE);
     535           0 :         return;
     536             :     }
     537             : 
     538             :     // PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE is allowed only for certain actions
     539           0 :     if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
     540           0 :         name = crm_element_value(action_config, PCMK_XA_NAME);
     541           0 :         role = crm_element_value(action_config, PCMK_XA_ROLE);
     542           0 :         interval_spec = crm_element_value(action_config, PCMK_META_INTERVAL);
     543           0 :         pcmk_parse_interval_spec(interval_spec, &interval_ms);
     544             : 
     545           0 :         if (!pcmk__str_eq(name, PCMK_ACTION_PROMOTE, pcmk__str_none)
     546           0 :             && ((interval_ms == 0U)
     547           0 :                 || !pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
     548           0 :                 || !pcmk__strcase_any_of(role, PCMK_ROLE_PROMOTED,
     549             :                                          PCMK__ROLE_PROMOTED_LEGACY, NULL))) {
     550             : 
     551           0 :             pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s %s "
     552             :                              "action to default value because 'demote' is not "
     553             :                              "allowed for it", rsc->id, name);
     554           0 :             g_hash_table_remove(meta, PCMK_META_ON_FAIL);
     555           0 :             return;
     556             :         }
     557             :     }
     558             : }
     559             : 
     560             : static int
     561           0 : unpack_timeout(const char *value)
     562             : {
     563           0 :     long long timeout_ms = crm_get_msec(value);
     564             : 
     565           0 :     if (timeout_ms <= 0) {
     566           0 :         timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
     567             :     }
     568           0 :     return (int) QB_MIN(timeout_ms, INT_MAX);
     569             : }
     570             : 
     571             : // true if value contains valid, non-NULL interval origin for recurring op
     572             : static bool
     573           0 : unpack_interval_origin(const char *value, const xmlNode *xml_obj,
     574             :                        guint interval_ms, const crm_time_t *now,
     575             :                        long long *start_delay)
     576             : {
     577           0 :     long long result = 0;
     578           0 :     guint interval_sec = interval_ms / 1000;
     579           0 :     crm_time_t *origin = NULL;
     580             : 
     581             :     // Ignore unspecified values and non-recurring operations
     582           0 :     if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
     583           0 :         return false;
     584             :     }
     585             : 
     586             :     // Parse interval origin from text
     587           0 :     origin = crm_time_new(value);
     588           0 :     if (origin == NULL) {
     589           0 :         pcmk__config_err("Ignoring '" PCMK_META_INTERVAL_ORIGIN "' for "
     590             :                          "operation '%s' because '%s' is not valid",
     591             :                          pcmk__s(pcmk__xe_id(xml_obj), "(missing ID)"), value);
     592           0 :         return false;
     593             :     }
     594             : 
     595             :     // Get seconds since origin (negative if origin is in the future)
     596           0 :     result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
     597           0 :     crm_time_free(origin);
     598             : 
     599             :     // Calculate seconds from closest interval to now
     600           0 :     result = result % interval_sec;
     601             : 
     602             :     // Calculate seconds remaining until next interval
     603           0 :     result = ((result <= 0)? 0 : interval_sec) - result;
     604           0 :     crm_info("Calculated a start delay of %llds for operation '%s'",
     605             :              result, pcmk__s(pcmk__xe_id(xml_obj), "(unspecified)"));
     606             : 
     607           0 :     if (start_delay != NULL) {
     608           0 :         *start_delay = result * 1000; // milliseconds
     609             :     }
     610           0 :     return true;
     611             : }
     612             : 
     613             : static int
     614           0 : unpack_start_delay(const char *value, GHashTable *meta)
     615             : {
     616           0 :     long long start_delay_ms = 0;
     617             : 
     618           0 :     if (value == NULL) {
     619           0 :         return 0;
     620             :     }
     621             : 
     622           0 :     start_delay_ms = crm_get_msec(value);
     623           0 :     start_delay_ms = QB_MIN(start_delay_ms, INT_MAX);
     624           0 :     if (start_delay_ms < 0) {
     625           0 :         start_delay_ms = 0;
     626             :     }
     627             : 
     628           0 :     if (meta != NULL) {
     629           0 :         g_hash_table_replace(meta, strdup(PCMK_META_START_DELAY),
     630           0 :                              pcmk__itoa(start_delay_ms));
     631             :     }
     632             : 
     633           0 :     return (int) start_delay_ms;
     634             : }
     635             : 
     636             : /*!
     637             :  * \internal
     638             :  * \brief Find a resource's most frequent recurring monitor
     639             :  *
     640             :  * \param[in] rsc  Resource to check
     641             :  *
     642             :  * \return Operation XML configured for most frequent recurring monitor for
     643             :  *         \p rsc (if any)
     644             :  */
     645             : static xmlNode *
     646           0 : most_frequent_monitor(const pcmk_resource_t *rsc)
     647             : {
     648           0 :     guint min_interval_ms = G_MAXUINT;
     649           0 :     xmlNode *op = NULL;
     650             : 
     651           0 :     for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
     652             :                                                    NULL, NULL);
     653           0 :          operation != NULL; operation = pcmk__xe_next_same(operation)) {
     654             : 
     655           0 :         bool enabled = false;
     656           0 :         guint interval_ms = 0U;
     657           0 :         const char *interval_spec = crm_element_value(operation,
     658             :                                                       PCMK_META_INTERVAL);
     659             : 
     660             :         // We only care about enabled recurring monitors
     661           0 :         if (!pcmk__str_eq(crm_element_value(operation, PCMK_XA_NAME),
     662             :                           PCMK_ACTION_MONITOR, pcmk__str_none)) {
     663           0 :             continue;
     664             :         }
     665             : 
     666           0 :         pcmk_parse_interval_spec(interval_spec, &interval_ms);
     667           0 :         if (interval_ms == 0U) {
     668           0 :             continue;
     669             :         }
     670             : 
     671             :         // @TODO This does not consider meta-attributes, rules, defaults, etc.
     672           0 :         if ((pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED,
     673           0 :                                     &enabled) == pcmk_rc_ok) && !enabled) {
     674           0 :             continue;
     675             :         }
     676             : 
     677           0 :         if (interval_ms < min_interval_ms) {
     678           0 :             min_interval_ms = interval_ms;
     679           0 :             op = operation;
     680             :         }
     681             :     }
     682           0 :     return op;
     683             : }
     684             : 
     685             : /*!
     686             :  * \internal
     687             :  * \brief Unpack action meta-attributes
     688             :  *
     689             :  * \param[in,out] rsc            Resource that action is for
     690             :  * \param[in]     node           Node that action is on
     691             :  * \param[in]     action_name    Action name
     692             :  * \param[in]     interval_ms    Action interval (in milliseconds)
     693             :  * \param[in]     action_config  Action XML configuration from CIB (if any)
     694             :  *
     695             :  * Unpack a resource action's meta-attributes (normalizing the interval,
     696             :  * timeout, and start delay values as integer milliseconds) from its CIB XML
     697             :  * configuration (including defaults).
     698             :  *
     699             :  * \return Newly allocated hash table with normalized action meta-attributes
     700             :  */
     701             : GHashTable *
     702           0 : pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node,
     703             :                          const char *action_name, guint interval_ms,
     704             :                          const xmlNode *action_config)
     705             : {
     706           0 :     GHashTable *meta = NULL;
     707           0 :     const char *timeout_spec = NULL;
     708           0 :     const char *str = NULL;
     709             : 
     710           0 :     pe_rsc_eval_data_t rsc_rule_data = {
     711           0 :         .standard = crm_element_value(rsc->xml, PCMK_XA_CLASS),
     712           0 :         .provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER),
     713           0 :         .agent = crm_element_value(rsc->xml, PCMK_XA_TYPE),
     714             :     };
     715             : 
     716           0 :     pe_op_eval_data_t op_rule_data = {
     717             :         .op_name = action_name,
     718             :         .interval = interval_ms,
     719             :     };
     720             : 
     721           0 :     pe_rule_eval_data_t rule_data = {
     722             :         /* @COMPAT Support for node attribute expressions in operation
     723             :          * meta-attributes (whether in the operation configuration or operation
     724             :          * defaults) is deprecated. When we can break behavioral backward
     725             :          * compatibility, drop this line.
     726             :          */
     727           0 :         .node_hash = (node == NULL)? NULL : node->details->attrs,
     728             : 
     729           0 :         .now = rsc->cluster->now,
     730             :         .match_data = NULL,
     731             :         .rsc_data = &rsc_rule_data,
     732             :         .op_data = &op_rule_data,
     733             :     };
     734             : 
     735           0 :     meta = pcmk__strkey_table(free, free);
     736             : 
     737             :     // Cluster-wide <op_defaults> <meta_attributes>
     738           0 :     pe__unpack_dataset_nvpairs(rsc->cluster->op_defaults,
     739             :                                PCMK_XE_META_ATTRIBUTES, &rule_data, meta, NULL,
     740             :                                FALSE, rsc->cluster);
     741             : 
     742             :     // Derive default timeout for probes from recurring monitor timeouts
     743           0 :     if (pcmk_is_probe(action_name, interval_ms)) {
     744           0 :         xmlNode *min_interval_mon = most_frequent_monitor(rsc);
     745             : 
     746           0 :         if (min_interval_mon != NULL) {
     747             :             /* @TODO This does not consider timeouts set in
     748             :              * PCMK_XE_META_ATTRIBUTES blocks (which may also have rules that
     749             :              * need to be evaluated).
     750             :              */
     751           0 :             timeout_spec = crm_element_value(min_interval_mon,
     752             :                                              PCMK_META_TIMEOUT);
     753           0 :             if (timeout_spec != NULL) {
     754           0 :                 pcmk__rsc_trace(rsc,
     755             :                                 "Setting default timeout for %s probe to "
     756             :                                 "most frequent monitor's timeout '%s'",
     757             :                                 rsc->id, timeout_spec);
     758           0 :                 pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec);
     759             :             }
     760             :         }
     761             :     }
     762             : 
     763           0 :     if (action_config != NULL) {
     764             :         // <op> <meta_attributes> take precedence over defaults
     765           0 :         pe__unpack_dataset_nvpairs(action_config, PCMK_XE_META_ATTRIBUTES,
     766             :                                    &rule_data, meta, NULL, TRUE, rsc->cluster);
     767             : 
     768             :         /* Anything set as an <op> XML property has highest precedence.
     769             :          * This ensures we use the name and interval from the <op> tag.
     770             :          * (See below for the only exception, fence device start/probe timeout.)
     771             :          */
     772           0 :         for (xmlAttrPtr attr = action_config->properties;
     773           0 :              attr != NULL; attr = attr->next) {
     774           0 :             pcmk__insert_dup(meta, (const char *) attr->name,
     775             :                              pcmk__xml_attr_value(attr));
     776             :         }
     777             :     }
     778             : 
     779           0 :     g_hash_table_remove(meta, PCMK_XA_ID);
     780             : 
     781             :     // Normalize interval to milliseconds
     782           0 :     if (interval_ms > 0) {
     783           0 :         g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_INTERVAL),
     784           0 :                             crm_strdup_printf("%u", interval_ms));
     785             :     } else {
     786           0 :         g_hash_table_remove(meta, PCMK_META_INTERVAL);
     787             :     }
     788             : 
     789             :     /* Timeout order of precedence (highest to lowest):
     790             :      *   1. pcmk_monitor_timeout resource parameter (only for starts and probes
     791             :      *      when rsc has pcmk_ra_cap_fence_params; this gets used for recurring
     792             :      *      monitors via the executor instead)
     793             :      *   2. timeout configured in <op> (with <op timeout> taking precedence over
     794             :      *      <op> <meta_attributes>)
     795             :      *   3. timeout configured in <op_defaults> <meta_attributes>
     796             :      *   4. PCMK_DEFAULT_ACTION_TIMEOUT_MS
     797             :      */
     798             : 
     799             :     // Check for pcmk_monitor_timeout
     800           0 :     if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
     801             :                     pcmk_ra_cap_fence_params)
     802           0 :         && (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)
     803           0 :             || pcmk_is_probe(action_name, interval_ms))) {
     804             : 
     805           0 :         GHashTable *params = pe_rsc_params(rsc, node, rsc->cluster);
     806             : 
     807           0 :         timeout_spec = g_hash_table_lookup(params, "pcmk_monitor_timeout");
     808           0 :         if (timeout_spec != NULL) {
     809           0 :             pcmk__rsc_trace(rsc,
     810             :                             "Setting timeout for %s %s to "
     811             :                             "pcmk_monitor_timeout (%s)",
     812             :                             rsc->id, action_name, timeout_spec);
     813           0 :             pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec);
     814             :         }
     815             :     }
     816             : 
     817             :     // Normalize timeout to positive milliseconds
     818           0 :     timeout_spec = g_hash_table_lookup(meta, PCMK_META_TIMEOUT);
     819           0 :     g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_TIMEOUT),
     820           0 :                         pcmk__itoa(unpack_timeout(timeout_spec)));
     821             : 
     822             :     // Ensure on-fail has a valid value
     823           0 :     validate_on_fail(rsc, action_name, action_config, meta);
     824             : 
     825             :     // Normalize PCMK_META_START_DELAY
     826           0 :     str = g_hash_table_lookup(meta, PCMK_META_START_DELAY);
     827           0 :     if (str != NULL) {
     828           0 :         unpack_start_delay(str, meta);
     829             :     } else {
     830           0 :         long long start_delay = 0;
     831             : 
     832           0 :         str = g_hash_table_lookup(meta, PCMK_META_INTERVAL_ORIGIN);
     833           0 :         if (unpack_interval_origin(str, action_config, interval_ms,
     834           0 :                                    rsc->cluster->now, &start_delay)) {
     835           0 :             g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_START_DELAY),
     836           0 :                                 crm_strdup_printf("%lld", start_delay));
     837             :         }
     838             :     }
     839           0 :     return meta;
     840             : }
     841             : 
     842             : /*!
     843             :  * \internal
     844             :  * \brief Determine an action's quorum and fencing dependency
     845             :  *
     846             :  * \param[in] rsc          Resource that action is for
     847             :  * \param[in] action_name  Name of action being unpacked
     848             :  *
     849             :  * \return Quorum and fencing dependency appropriate to action
     850             :  */
     851             : enum rsc_start_requirement
     852           0 : pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
     853             : {
     854           0 :     const char *value = NULL;
     855           0 :     enum rsc_start_requirement requires = pcmk_requires_nothing;
     856             : 
     857           0 :     CRM_CHECK((rsc != NULL) && (action_name != NULL), return requires);
     858             : 
     859           0 :     if (!pcmk__strcase_any_of(action_name, PCMK_ACTION_START,
     860             :                               PCMK_ACTION_PROMOTE, NULL)) {
     861           0 :         value = "nothing (not start or promote)";
     862             : 
     863           0 :     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
     864           0 :         requires = pcmk_requires_fencing;
     865           0 :         value = "fencing";
     866             : 
     867           0 :     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_quorum)) {
     868           0 :         requires = pcmk_requires_quorum;
     869           0 :         value = "quorum";
     870             : 
     871             :     } else {
     872           0 :         value = "nothing";
     873             :     }
     874           0 :     pcmk__rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value);
     875           0 :     return requires;
     876             : }
     877             : 
     878             : /*!
     879             :  * \internal
     880             :  * \brief Parse action failure response from a user-provided string
     881             :  *
     882             :  * \param[in] rsc          Resource that action is for
     883             :  * \param[in] action_name  Name of action
     884             :  * \param[in] interval_ms  Action interval (in milliseconds)
     885             :  * \param[in] value        User-provided configuration value for on-fail
     886             :  *
     887             :  * \return Action failure response parsed from \p text
     888             :  */
     889             : enum action_fail_response
     890           0 : pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name,
     891             :                     guint interval_ms, const char *value)
     892             : {
     893           0 :     const char *desc = NULL;
     894           0 :     bool needs_remote_reset = false;
     895           0 :     enum action_fail_response on_fail = pcmk_on_fail_ignore;
     896             : 
     897             :     // There's no enum value for unknown or invalid, so assert
     898           0 :     CRM_ASSERT((rsc != NULL) && (action_name != NULL));
     899             : 
     900           0 :     if (value == NULL) {
     901             :         // Use default
     902             : 
     903           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) {
     904           0 :         on_fail = pcmk_on_fail_block;
     905           0 :         desc = "block";
     906             : 
     907           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_FENCE, pcmk__str_casei)) {
     908           0 :         if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
     909           0 :             on_fail = pcmk_on_fail_fence_node;
     910           0 :             desc = "node fencing";
     911             :         } else {
     912           0 :             pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for "
     913             :                              "%s of %s to 'stop' because 'fence' is not "
     914             :                              "valid when fencing is disabled",
     915             :                              action_name, rsc->id);
     916           0 :             on_fail = pcmk_on_fail_stop;
     917           0 :             desc = "stop resource";
     918             :         }
     919             : 
     920           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_STANDBY, pcmk__str_casei)) {
     921           0 :         on_fail = pcmk_on_fail_standby_node;
     922           0 :         desc = "node standby";
     923             : 
     924           0 :     } else if (pcmk__strcase_any_of(value,
     925             :                                     PCMK_VALUE_IGNORE, PCMK_VALUE_NOTHING,
     926             :                                     NULL)) {
     927           0 :         desc = "ignore";
     928             : 
     929           0 :     } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
     930           0 :         on_fail = pcmk_on_fail_ban;
     931           0 :         desc = "force migration";
     932             : 
     933           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_STOP, pcmk__str_casei)) {
     934           0 :         on_fail = pcmk_on_fail_stop;
     935           0 :         desc = "stop resource";
     936             : 
     937           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) {
     938           0 :         on_fail = pcmk_on_fail_restart;
     939           0 :         desc = "restart (and possibly migrate)";
     940             : 
     941           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART_CONTAINER,
     942             :                             pcmk__str_casei)) {
     943           0 :         if (rsc->container == NULL) {
     944           0 :             pcmk__rsc_debug(rsc,
     945             :                             "Using default " PCMK_META_ON_FAIL " for %s "
     946             :                             "of %s because it does not have a container",
     947             :                             action_name, rsc->id);
     948             :         } else {
     949           0 :             on_fail = pcmk_on_fail_restart_container;
     950           0 :             desc = "restart container (and possibly migrate)";
     951             :         }
     952             : 
     953           0 :     } else if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
     954           0 :         on_fail = pcmk_on_fail_demote;
     955           0 :         desc = "demote instance";
     956             : 
     957             :     } else {
     958           0 :         pcmk__config_err("Using default '" PCMK_META_ON_FAIL "' for "
     959             :                          "%s of %s because '%s' is not valid",
     960             :                          action_name, rsc->id, value);
     961             :     }
     962             : 
     963             :     /* Remote node connections are handled specially. Failures that result
     964             :      * in dropping an active connection must result in fencing. The only
     965             :      * failures that don't are probes and starts. The user can explicitly set
     966             :      * PCMK_META_ON_FAIL=PCMK_VALUE_FENCE to fence after start failures.
     967             :      */
     968           0 :     if (rsc->is_remote_node
     969           0 :         && pcmk__is_remote_node(pcmk_find_node(rsc->cluster, rsc->id))
     970           0 :         && !pcmk_is_probe(action_name, interval_ms)
     971           0 :         && !pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
     972           0 :         needs_remote_reset = true;
     973           0 :         if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     974           0 :             desc = NULL; // Force default for unmanaged connections
     975             :         }
     976             :     }
     977             : 
     978           0 :     if (desc != NULL) {
     979             :         // Explicit value used, default not needed
     980             : 
     981           0 :     } else if (rsc->container != NULL) {
     982           0 :         on_fail = pcmk_on_fail_restart_container;
     983           0 :         desc = "restart container (and possibly migrate) (default)";
     984             : 
     985           0 :     } else if (needs_remote_reset) {
     986           0 :         if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     987           0 :             if (pcmk_is_set(rsc->cluster->flags,
     988             :                             pcmk_sched_fencing_enabled)) {
     989           0 :                 desc = "fence remote node (default)";
     990             :             } else {
     991           0 :                 desc = "recover remote node connection (default)";
     992             :             }
     993           0 :             on_fail = pcmk_on_fail_reset_remote;
     994             :         } else {
     995           0 :             on_fail = pcmk_on_fail_stop;
     996           0 :             desc = "stop unmanaged remote node (enforcing default)";
     997             :         }
     998             : 
     999           0 :     } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
    1000           0 :         if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
    1001           0 :             on_fail = pcmk_on_fail_fence_node;
    1002           0 :             desc = "resource fence (default)";
    1003             :         } else {
    1004           0 :             on_fail = pcmk_on_fail_block;
    1005           0 :             desc = "resource block (default)";
    1006             :         }
    1007             : 
    1008             :     } else {
    1009           0 :         on_fail = pcmk_on_fail_restart;
    1010           0 :         desc = "restart (and possibly migrate) (default)";
    1011             :     }
    1012             : 
    1013           0 :     pcmk__rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s",
    1014             :                     pcmk__readable_interval(interval_ms), action_name,
    1015             :                     rsc->id, desc);
    1016           0 :     return on_fail;
    1017             : }
    1018             : 
    1019             : /*!
    1020             :  * \internal
    1021             :  * \brief Determine a resource's role after failure of an action
    1022             :  *
    1023             :  * \param[in] rsc          Resource that action is for
    1024             :  * \param[in] action_name  Action name
    1025             :  * \param[in] on_fail      Failure handling for action
    1026             :  * \param[in] meta         Unpacked action meta-attributes
    1027             :  *
    1028             :  * \return Resource role that results from failure of action
    1029             :  */
    1030             : enum rsc_role_e
    1031           0 : pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name,
    1032             :                          enum action_fail_response on_fail, GHashTable *meta)
    1033             : {
    1034           0 :     const char *value = NULL;
    1035           0 :     enum rsc_role_e role = pcmk_role_unknown;
    1036             : 
    1037             :     // Set default for role after failure specially in certain circumstances
    1038           0 :     switch (on_fail) {
    1039           0 :         case pcmk_on_fail_stop:
    1040           0 :             role = pcmk_role_stopped;
    1041           0 :             break;
    1042             : 
    1043           0 :         case pcmk_on_fail_reset_remote:
    1044           0 :             if (rsc->remote_reconnect_ms != 0) {
    1045           0 :                 role = pcmk_role_stopped;
    1046             :             }
    1047           0 :             break;
    1048             : 
    1049           0 :         default:
    1050           0 :             break;
    1051             :     }
    1052             : 
    1053             :     // @COMPAT Check for explicitly configured role (deprecated)
    1054           0 :     value = g_hash_table_lookup(meta, PCMK__META_ROLE_AFTER_FAILURE);
    1055           0 :     if (value != NULL) {
    1056           0 :         pcmk__warn_once(pcmk__wo_role_after,
    1057             :                         "Support for " PCMK__META_ROLE_AFTER_FAILURE " is "
    1058             :                         "deprecated and will be removed in a future release");
    1059           0 :         if (role == pcmk_role_unknown) {
    1060           0 :             role = pcmk_parse_role(value);
    1061           0 :             if (role == pcmk_role_unknown) {
    1062           0 :                 pcmk__config_err("Ignoring invalid value %s for "
    1063             :                                  PCMK__META_ROLE_AFTER_FAILURE,
    1064             :                                  value);
    1065             :             }
    1066             :         }
    1067             :     }
    1068             : 
    1069           0 :     if (role == pcmk_role_unknown) {
    1070             :         // Use default
    1071           0 :         if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
    1072           0 :             role = pcmk_role_unpromoted;
    1073             :         } else {
    1074           0 :             role = pcmk_role_started;
    1075             :         }
    1076             :     }
    1077           0 :     pcmk__rsc_trace(rsc, "Role after %s %s failure is: %s",
    1078             :                     rsc->id, action_name, pcmk_role_text(role));
    1079           0 :     return role;
    1080             : }
    1081             : 
    1082             : /*!
    1083             :  * \internal
    1084             :  * \brief Unpack action configuration
    1085             :  *
    1086             :  * Unpack a resource action's meta-attributes (normalizing the interval,
    1087             :  * timeout, and start delay values as integer milliseconds), requirements, and
    1088             :  * failure policy from its CIB XML configuration (including defaults).
    1089             :  *
    1090             :  * \param[in,out] action       Resource action to unpack into
    1091             :  * \param[in]     xml_obj      Action configuration XML (NULL for defaults only)
    1092             :  * \param[in]     interval_ms  How frequently to perform the operation
    1093             :  */
    1094             : static void
    1095           0 : unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
    1096             :                  guint interval_ms)
    1097             : {
    1098           0 :     const char *value = NULL;
    1099             : 
    1100           0 :     action->meta = pcmk__unpack_action_meta(action->rsc, action->node,
    1101           0 :                                             action->task, interval_ms, xml_obj);
    1102           0 :     action->needs = pcmk__action_requires(action->rsc, action->task);
    1103             : 
    1104           0 :     value = g_hash_table_lookup(action->meta, PCMK_META_ON_FAIL);
    1105           0 :     action->on_fail = pcmk__parse_on_fail(action->rsc, action->task,
    1106             :                                           interval_ms, value);
    1107             : 
    1108           0 :     action->fail_role = pcmk__role_after_failure(action->rsc, action->task,
    1109             :                                                  action->on_fail, action->meta);
    1110           0 : }
    1111             : 
    1112             : /*!
    1113             :  * \brief Create or update an action object
    1114             :  *
    1115             :  * \param[in,out] rsc          Resource that action is for (if any)
    1116             :  * \param[in,out] key          Action key (must be non-NULL)
    1117             :  * \param[in]     task         Action name (must be non-NULL)
    1118             :  * \param[in]     on_node      Node that action is on (if any)
    1119             :  * \param[in]     optional     Whether action should be considered optional
    1120             :  * \param[in,out] scheduler    Scheduler data
    1121             :  *
    1122             :  * \return Action object corresponding to arguments (guaranteed not to be
    1123             :  *         \c NULL)
    1124             :  * \note This function takes ownership of (and might free) \p key, and
    1125             :  *       \p scheduler takes ownership of the returned action (the caller should
    1126             :  *       not free it).
    1127             :  */
    1128             : pcmk_action_t *
    1129           0 : custom_action(pcmk_resource_t *rsc, char *key, const char *task,
    1130             :               const pcmk_node_t *on_node, gboolean optional,
    1131             :               pcmk_scheduler_t *scheduler)
    1132             : {
    1133           0 :     pcmk_action_t *action = NULL;
    1134             : 
    1135           0 :     CRM_ASSERT((key != NULL) && (task != NULL) && (scheduler != NULL));
    1136             : 
    1137           0 :     action = find_existing_action(key, rsc, on_node, scheduler);
    1138           0 :     if (action == NULL) {
    1139           0 :         action = new_action(key, task, rsc, on_node, optional, scheduler);
    1140             :     } else {
    1141           0 :         free(key);
    1142             :     }
    1143             : 
    1144           0 :     update_action_optional(action, optional);
    1145             : 
    1146           0 :     if (rsc != NULL) {
    1147             :         /* An action can be initially created with a NULL node, and later have
    1148             :          * the node added via find_existing_action() (above) -> find_actions().
    1149             :          * That is why the extra parameters are unpacked here rather than in
    1150             :          * new_action().
    1151             :          */
    1152           0 :         if ((action->node != NULL) && (action->op_entry != NULL)
    1153           0 :             && !pcmk_is_set(action->flags, pcmk_action_attrs_evaluated)) {
    1154             : 
    1155           0 :             GHashTable *attrs = action->node->details->attrs;
    1156             : 
    1157           0 :             if (action->extra != NULL) {
    1158           0 :                 g_hash_table_destroy(action->extra);
    1159             :             }
    1160           0 :             action->extra = pcmk__unpack_action_rsc_params(action->op_entry,
    1161             :                                                            attrs, scheduler);
    1162           0 :             pcmk__set_action_flags(action, pcmk_action_attrs_evaluated);
    1163             :         }
    1164             : 
    1165           0 :         update_resource_action_runnable(action, scheduler);
    1166           0 :         update_resource_flags_for_action(rsc, action);
    1167             :     }
    1168             : 
    1169           0 :     if (action->extra == NULL) {
    1170           0 :         action->extra = pcmk__strkey_table(free, free);
    1171             :     }
    1172             : 
    1173           0 :     return action;
    1174             : }
    1175             : 
    1176             : pcmk_action_t *
    1177           0 : get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
    1178             : {
    1179           0 :     pcmk_action_t *op = lookup_singleton(scheduler, name);
    1180             : 
    1181           0 :     if (op == NULL) {
    1182           0 :         op = custom_action(NULL, strdup(name), name, NULL, TRUE, scheduler);
    1183           0 :         pcmk__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable);
    1184             :     }
    1185           0 :     return op;
    1186             : }
    1187             : 
    1188             : static GList *
    1189           0 : find_unfencing_devices(GList *candidates, GList *matches) 
    1190             : {
    1191           0 :     for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
    1192           0 :         pcmk_resource_t *candidate = gIter->data;
    1193             : 
    1194           0 :         if (candidate->children != NULL) {
    1195           0 :             matches = find_unfencing_devices(candidate->children, matches);
    1196             : 
    1197           0 :         } else if (!pcmk_is_set(candidate->flags, pcmk_rsc_fence_device)) {
    1198           0 :             continue;
    1199             : 
    1200           0 :         } else if (pcmk_is_set(candidate->flags, pcmk_rsc_needs_unfencing)) {
    1201           0 :             matches = g_list_prepend(matches, candidate);
    1202             : 
    1203           0 :         } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
    1204             :                                                     PCMK_STONITH_PROVIDES),
    1205             :                                 PCMK_VALUE_UNFENCING, pcmk__str_casei)) {
    1206           0 :             matches = g_list_prepend(matches, candidate);
    1207             :         }
    1208             :     }
    1209           0 :     return matches;
    1210             : }
    1211             : 
    1212             : static int
    1213           0 : node_priority_fencing_delay(const pcmk_node_t *node,
    1214             :                             const pcmk_scheduler_t *scheduler)
    1215             : {
    1216           0 :     int member_count = 0;
    1217           0 :     int online_count = 0;
    1218           0 :     int top_priority = 0;
    1219           0 :     int lowest_priority = 0;
    1220           0 :     GList *gIter = NULL;
    1221             : 
    1222             :     // PCMK_OPT_PRIORITY_FENCING_DELAY is disabled
    1223           0 :     if (scheduler->priority_fencing_delay <= 0) {
    1224           0 :         return 0;
    1225             :     }
    1226             : 
    1227             :     /* No need to request a delay if the fencing target is not a normal cluster
    1228             :      * member, for example if it's a remote node or a guest node. */
    1229           0 :     if (node->details->type != pcmk_node_variant_cluster) {
    1230           0 :         return 0;
    1231             :     }
    1232             : 
    1233             :     // No need to request a delay if the fencing target is in our partition
    1234           0 :     if (node->details->online) {
    1235           0 :         return 0;
    1236             :     }
    1237             : 
    1238           0 :     for (gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
    1239           0 :         pcmk_node_t *n = gIter->data;
    1240             : 
    1241           0 :         if (n->details->type != pcmk_node_variant_cluster) {
    1242           0 :             continue;
    1243             :         }
    1244             : 
    1245           0 :         member_count ++;
    1246             : 
    1247           0 :         if (n->details->online) {
    1248           0 :             online_count++;
    1249             :         }
    1250             : 
    1251           0 :         if (member_count == 1
    1252           0 :             || n->details->priority > top_priority) {
    1253           0 :             top_priority = n->details->priority;
    1254             :         }
    1255             : 
    1256           0 :         if (member_count == 1
    1257           0 :             || n->details->priority < lowest_priority) {
    1258           0 :             lowest_priority = n->details->priority;
    1259             :         }
    1260             :     }
    1261             : 
    1262             :     // No need to delay if we have more than half of the cluster members
    1263           0 :     if (online_count > member_count / 2) {
    1264           0 :         return 0;
    1265             :     }
    1266             : 
    1267             :     /* All the nodes have equal priority.
    1268             :      * Any configured corresponding `pcmk_delay_base/max` will be applied. */
    1269           0 :     if (lowest_priority == top_priority) {
    1270           0 :         return 0;
    1271             :     }
    1272             : 
    1273           0 :     if (node->details->priority < top_priority) {
    1274           0 :         return 0;
    1275             :     }
    1276             : 
    1277           0 :     return scheduler->priority_fencing_delay;
    1278             : }
    1279             : 
    1280             : pcmk_action_t *
    1281           0 : pe_fence_op(pcmk_node_t *node, const char *op, bool optional,
    1282             :             const char *reason, bool priority_delay,
    1283             :             pcmk_scheduler_t *scheduler)
    1284             : {
    1285           0 :     char *op_key = NULL;
    1286           0 :     pcmk_action_t *stonith_op = NULL;
    1287             : 
    1288           0 :     if(op == NULL) {
    1289           0 :         op = scheduler->stonith_action;
    1290             :     }
    1291             : 
    1292           0 :     op_key = crm_strdup_printf("%s-%s-%s",
    1293           0 :                                PCMK_ACTION_STONITH, node->details->uname, op);
    1294             : 
    1295           0 :     stonith_op = lookup_singleton(scheduler, op_key);
    1296           0 :     if(stonith_op == NULL) {
    1297           0 :         stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node,
    1298             :                                    TRUE, scheduler);
    1299             : 
    1300           0 :         pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE, node->details->uname);
    1301           0 :         pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE_UUID,
    1302             :                           node->details->id);
    1303           0 :         pcmk__insert_meta(stonith_op, PCMK__META_STONITH_ACTION, op);
    1304             : 
    1305           0 :         if (pcmk_is_set(scheduler->flags, pcmk_sched_enable_unfencing)) {
    1306             :             /* Extra work to detect device changes
    1307             :              */
    1308           0 :             GString *digests_all = g_string_sized_new(1024);
    1309           0 :             GString *digests_secure = g_string_sized_new(1024);
    1310             : 
    1311           0 :             GList *matches = find_unfencing_devices(scheduler->resources, NULL);
    1312             : 
    1313           0 :             for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
    1314           0 :                 pcmk_resource_t *match = gIter->data;
    1315           0 :                 const char *agent = g_hash_table_lookup(match->meta,
    1316             :                                                         PCMK_XA_TYPE);
    1317           0 :                 pcmk__op_digest_t *data = NULL;
    1318             : 
    1319           0 :                 data = pe__compare_fencing_digest(match, agent, node,
    1320             :                                                   scheduler);
    1321           0 :                 if (data->rc == pcmk__digest_mismatch) {
    1322           0 :                     optional = FALSE;
    1323           0 :                     crm_notice("Unfencing node %s because the definition of "
    1324             :                                "%s changed", pcmk__node_name(node), match->id);
    1325           0 :                     if (!pcmk__is_daemon && scheduler->priv != NULL) {
    1326           0 :                         pcmk__output_t *out = scheduler->priv;
    1327             : 
    1328           0 :                         out->info(out,
    1329             :                                   "notice: Unfencing node %s because the "
    1330             :                                   "definition of %s changed",
    1331             :                                   pcmk__node_name(node), match->id);
    1332             :                     }
    1333             :                 }
    1334             : 
    1335           0 :                 pcmk__g_strcat(digests_all,
    1336             :                                match->id, ":", agent, ":",
    1337             :                                data->digest_all_calc, ",", NULL);
    1338           0 :                 pcmk__g_strcat(digests_secure,
    1339             :                                match->id, ":", agent, ":",
    1340             :                                data->digest_secure_calc, ",", NULL);
    1341             :             }
    1342           0 :             pcmk__insert_dup(stonith_op->meta, PCMK__META_DIGESTS_ALL,
    1343           0 :                              digests_all->str);
    1344           0 :             g_string_free(digests_all, TRUE);
    1345             : 
    1346           0 :             pcmk__insert_dup(stonith_op->meta, PCMK__META_DIGESTS_SECURE,
    1347           0 :                              digests_secure->str);
    1348           0 :             g_string_free(digests_secure, TRUE);
    1349             :         }
    1350             : 
    1351             :     } else {
    1352           0 :         free(op_key);
    1353             :     }
    1354             : 
    1355           0 :     if (scheduler->priority_fencing_delay > 0
    1356             : 
    1357             :             /* It's a suitable case where PCMK_OPT_PRIORITY_FENCING_DELAY
    1358             :              * applies. At least add PCMK_OPT_PRIORITY_FENCING_DELAY field as
    1359             :              * an indicator.
    1360             :              */
    1361           0 :         && (priority_delay
    1362             : 
    1363             :             /* The priority delay needs to be recalculated if this function has
    1364             :              * been called by schedule_fencing_and_shutdowns() after node
    1365             :              * priority has already been calculated by native_add_running().
    1366             :              */
    1367           0 :             || g_hash_table_lookup(stonith_op->meta,
    1368             :                                    PCMK_OPT_PRIORITY_FENCING_DELAY) != NULL)) {
    1369             : 
    1370             :             /* Add PCMK_OPT_PRIORITY_FENCING_DELAY to the fencing op even if
    1371             :              * it's 0 for the targeting node. So that it takes precedence over
    1372             :              * any possible `pcmk_delay_base/max`.
    1373             :              */
    1374           0 :             char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
    1375             :                                                                    scheduler));
    1376             : 
    1377           0 :             g_hash_table_insert(stonith_op->meta,
    1378           0 :                                 strdup(PCMK_OPT_PRIORITY_FENCING_DELAY),
    1379             :                                 delay_s);
    1380             :     }
    1381             : 
    1382           0 :     if(optional == FALSE && pe_can_fence(scheduler, node)) {
    1383           0 :         pcmk__clear_action_flags(stonith_op, pcmk_action_optional);
    1384           0 :         pe_action_set_reason(stonith_op, reason, false);
    1385             : 
    1386           0 :     } else if(reason && stonith_op->reason == NULL) {
    1387           0 :         stonith_op->reason = strdup(reason);
    1388             :     }
    1389             : 
    1390           0 :     return stonith_op;
    1391             : }
    1392             : 
    1393             : void
    1394           0 : pe_free_action(pcmk_action_t *action)
    1395             : {
    1396           0 :     if (action == NULL) {
    1397           0 :         return;
    1398             :     }
    1399           0 :     g_list_free_full(action->actions_before, free);
    1400           0 :     g_list_free_full(action->actions_after, free);
    1401           0 :     if (action->extra) {
    1402           0 :         g_hash_table_destroy(action->extra);
    1403             :     }
    1404           0 :     if (action->meta) {
    1405           0 :         g_hash_table_destroy(action->meta);
    1406             :     }
    1407           0 :     free(action->cancel_task);
    1408           0 :     free(action->reason);
    1409           0 :     free(action->task);
    1410           0 :     free(action->uuid);
    1411           0 :     free(action->node);
    1412           0 :     free(action);
    1413             : }
    1414             : 
    1415             : enum action_tasks
    1416           0 : get_complex_task(const pcmk_resource_t *rsc, const char *name)
    1417             : {
    1418           0 :     enum action_tasks task = pcmk_parse_action(name);
    1419             : 
    1420           0 :     if ((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)) {
    1421           0 :         switch (task) {
    1422           0 :             case pcmk_action_stopped:
    1423             :             case pcmk_action_started:
    1424             :             case pcmk_action_demoted:
    1425             :             case pcmk_action_promoted:
    1426           0 :                 crm_trace("Folding %s back into its atomic counterpart for %s",
    1427             :                           name, rsc->id);
    1428           0 :                 --task;
    1429           0 :                 break;
    1430           0 :             default:
    1431           0 :                 break;
    1432             :         }
    1433             :     }
    1434           0 :     return task;
    1435             : }
    1436             : 
    1437             : /*!
    1438             :  * \internal
    1439             :  * \brief Find first matching action in a list
    1440             :  *
    1441             :  * \param[in] input    List of actions to search
    1442             :  * \param[in] uuid     If not NULL, action must have this UUID
    1443             :  * \param[in] task     If not NULL, action must have this action name
    1444             :  * \param[in] on_node  If not NULL, action must be on this node
    1445             :  *
    1446             :  * \return First action in list that matches criteria, or NULL if none
    1447             :  */
    1448             : pcmk_action_t *
    1449           0 : find_first_action(const GList *input, const char *uuid, const char *task,
    1450             :                   const pcmk_node_t *on_node)
    1451             : {
    1452           0 :     CRM_CHECK(uuid || task, return NULL);
    1453             : 
    1454           0 :     for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
    1455           0 :         pcmk_action_t *action = (pcmk_action_t *) gIter->data;
    1456             : 
    1457           0 :         if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
    1458           0 :             continue;
    1459             : 
    1460           0 :         } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
    1461           0 :             continue;
    1462             : 
    1463           0 :         } else if (on_node == NULL) {
    1464           0 :             return action;
    1465             : 
    1466           0 :         } else if (action->node == NULL) {
    1467           0 :             continue;
    1468             : 
    1469           0 :         } else if (pcmk__same_node(on_node, action->node)) {
    1470           0 :             return action;
    1471             :         }
    1472             :     }
    1473             : 
    1474           0 :     return NULL;
    1475             : }
    1476             : 
    1477             : GList *
    1478           0 : find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
    1479             : {
    1480           0 :     GList *gIter = input;
    1481           0 :     GList *result = NULL;
    1482             : 
    1483           0 :     CRM_CHECK(key != NULL, return NULL);
    1484             : 
    1485           0 :     for (; gIter != NULL; gIter = gIter->next) {
    1486           0 :         pcmk_action_t *action = (pcmk_action_t *) gIter->data;
    1487             : 
    1488           0 :         if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
    1489           0 :             continue;
    1490             : 
    1491           0 :         } else if (on_node == NULL) {
    1492           0 :             crm_trace("Action %s matches (ignoring node)", key);
    1493           0 :             result = g_list_prepend(result, action);
    1494             : 
    1495           0 :         } else if (action->node == NULL) {
    1496           0 :             crm_trace("Action %s matches (unallocated, assigning to %s)",
    1497             :                       key, pcmk__node_name(on_node));
    1498             : 
    1499           0 :             action->node = pe__copy_node(on_node);
    1500           0 :             result = g_list_prepend(result, action);
    1501             : 
    1502           0 :         } else if (pcmk__same_node(on_node, action->node)) {
    1503           0 :             crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
    1504           0 :             result = g_list_prepend(result, action);
    1505             :         }
    1506             :     }
    1507             : 
    1508           0 :     return result;
    1509             : }
    1510             : 
    1511             : GList *
    1512           0 : find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
    1513             : {
    1514           0 :     GList *result = NULL;
    1515             : 
    1516           0 :     CRM_CHECK(key != NULL, return NULL);
    1517             : 
    1518           0 :     if (on_node == NULL) {
    1519           0 :         return NULL;
    1520             :     }
    1521             : 
    1522           0 :     for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
    1523           0 :         pcmk_action_t *action = (pcmk_action_t *) gIter->data;
    1524             : 
    1525           0 :         if ((action->node != NULL)
    1526           0 :             && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
    1527           0 :             && pcmk__str_eq(on_node->details->id, action->node->details->id,
    1528             :                             pcmk__str_casei)) {
    1529             : 
    1530           0 :             crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
    1531           0 :             result = g_list_prepend(result, action);
    1532             :         }
    1533             :     }
    1534             : 
    1535           0 :     return result;
    1536             : }
    1537             : 
    1538             : /*!
    1539             :  * \brief Find all actions of given type for a resource
    1540             :  *
    1541             :  * \param[in] rsc           Resource to search
    1542             :  * \param[in] node          Find only actions scheduled on this node
    1543             :  * \param[in] task          Action name to search for
    1544             :  * \param[in] require_node  If TRUE, NULL node or action node will not match
    1545             :  *
    1546             :  * \return List of actions found (or NULL if none)
    1547             :  * \note If node is not NULL and require_node is FALSE, matching actions
    1548             :  *       without a node will be assigned to node.
    1549             :  */
    1550             : GList *
    1551           0 : pe__resource_actions(const pcmk_resource_t *rsc, const pcmk_node_t *node,
    1552             :                      const char *task, bool require_node)
    1553             : {
    1554           0 :     GList *result = NULL;
    1555           0 :     char *key = pcmk__op_key(rsc->id, task, 0);
    1556             : 
    1557           0 :     if (require_node) {
    1558           0 :         result = find_actions_exact(rsc->actions, key, node);
    1559             :     } else {
    1560           0 :         result = find_actions(rsc->actions, key, node);
    1561             :     }
    1562           0 :     free(key);
    1563           0 :     return result;
    1564             : }
    1565             : 
    1566             : /*!
    1567             :  * \internal
    1568             :  * \brief Create an action reason string based on the action itself
    1569             :  *
    1570             :  * \param[in] action  Action to create reason string for
    1571             :  * \param[in] flag    Action flag that was cleared
    1572             :  *
    1573             :  * \return Newly allocated string suitable for use as action reason
    1574             :  * \note It is the caller's responsibility to free() the result.
    1575             :  */
    1576             : char *
    1577           0 : pe__action2reason(const pcmk_action_t *action, enum pe_action_flags flag)
    1578             : {
    1579           0 :     const char *change = NULL;
    1580             : 
    1581           0 :     switch (flag) {
    1582           0 :         case pcmk_action_runnable:
    1583           0 :             change = "unrunnable";
    1584           0 :             break;
    1585           0 :         case pcmk_action_migratable:
    1586           0 :             change = "unmigrateable";
    1587           0 :             break;
    1588           0 :         case pcmk_action_optional:
    1589           0 :             change = "required";
    1590           0 :             break;
    1591           0 :         default:
    1592             :             // Bug: caller passed unsupported flag
    1593           0 :             CRM_CHECK(change != NULL, change = "");
    1594           0 :             break;
    1595             :     }
    1596           0 :     return crm_strdup_printf("%s%s%s %s", change,
    1597           0 :                              (action->rsc == NULL)? "" : " ",
    1598           0 :                              (action->rsc == NULL)? "" : action->rsc->id,
    1599           0 :                              action->task);
    1600             : }
    1601             : 
    1602           0 : void pe_action_set_reason(pcmk_action_t *action, const char *reason,
    1603             :                           bool overwrite)
    1604             : {
    1605           0 :     if (action->reason != NULL && overwrite) {
    1606           0 :         pcmk__rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
    1607             :                         action->uuid, action->reason,
    1608             :                         pcmk__s(reason, "(none)"));
    1609           0 :     } else if (action->reason == NULL) {
    1610           0 :         pcmk__rsc_trace(action->rsc, "Set %s reason to '%s'",
    1611             :                         action->uuid, pcmk__s(reason, "(none)"));
    1612             :     } else {
    1613             :         // crm_assert(action->reason != NULL && !overwrite);
    1614           0 :         return;
    1615             :     }
    1616             : 
    1617           0 :     pcmk__str_update(&action->reason, reason);
    1618             : }
    1619             : 
    1620             : /*!
    1621             :  * \internal
    1622             :  * \brief Create an action to clear a resource's history from CIB
    1623             :  *
    1624             :  * \param[in,out] rsc       Resource to clear
    1625             :  * \param[in]     node      Node to clear history on
    1626             :  */
    1627             : void
    1628           0 : pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node)
    1629             : {
    1630           0 :     CRM_ASSERT((rsc != NULL) && (node != NULL));
    1631             : 
    1632           0 :     custom_action(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_LRM_DELETE, 0),
    1633             :                   PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->cluster);
    1634           0 : }
    1635             : 
    1636             : #define sort_return(an_int, why) do {                                   \
    1637             :         free(a_uuid);                                           \
    1638             :         free(b_uuid);                                           \
    1639             :         crm_trace("%s (%d) %c %s (%d) : %s",                          \
    1640             :                   a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',       \
    1641             :                   b_xml_id, b_call_id, why);                            \
    1642             :         return an_int;                                                  \
    1643             :     } while(0)
    1644             : 
    1645             : int
    1646           0 : pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
    1647             :                 bool same_node_default)
    1648             : {
    1649           0 :     int a_call_id = -1;
    1650           0 :     int b_call_id = -1;
    1651             : 
    1652           0 :     char *a_uuid = NULL;
    1653           0 :     char *b_uuid = NULL;
    1654             : 
    1655           0 :     const char *a_xml_id = crm_element_value(xml_a, PCMK_XA_ID);
    1656           0 :     const char *b_xml_id = crm_element_value(xml_b, PCMK_XA_ID);
    1657             : 
    1658           0 :     const char *a_node = crm_element_value(xml_a, PCMK__META_ON_NODE);
    1659           0 :     const char *b_node = crm_element_value(xml_b, PCMK__META_ON_NODE);
    1660           0 :     bool same_node = true;
    1661             : 
    1662             :     /* @COMPAT The on_node attribute was added to last_failure as of 1.1.13 (via
    1663             :      * 8b3ca1c) and the other entries as of 1.1.12 (via 0b07b5c).
    1664             :      *
    1665             :      * In case that any of the PCMK__XE_LRM_RSC_OP entries doesn't have on_node
    1666             :      * attribute, we need to explicitly tell whether the two operations are on
    1667             :      * the same node.
    1668             :      */
    1669           0 :     if (a_node == NULL || b_node == NULL) {
    1670           0 :         same_node = same_node_default;
    1671             : 
    1672             :     } else {
    1673           0 :         same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
    1674             :     }
    1675             : 
    1676           0 :     if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
    1677             :         /* We have duplicate PCMK__XE_LRM_RSC_OP entries in the status
    1678             :          * section which is unlikely to be a good thing
    1679             :          *    - we can handle it easily enough, but we need to get
    1680             :          *    to the bottom of why it's happening.
    1681             :          */
    1682           0 :         pcmk__config_err("Duplicate " PCMK__XE_LRM_RSC_OP " entries named %s",
    1683             :                          a_xml_id);
    1684           0 :         sort_return(0, "duplicate");
    1685             :     }
    1686             : 
    1687           0 :     crm_element_value_int(xml_a, PCMK__XA_CALL_ID, &a_call_id);
    1688           0 :     crm_element_value_int(xml_b, PCMK__XA_CALL_ID, &b_call_id);
    1689             : 
    1690           0 :     if (a_call_id == -1 && b_call_id == -1) {
    1691             :         /* both are pending ops so it doesn't matter since
    1692             :          *   stops are never pending
    1693             :          */
    1694           0 :         sort_return(0, "pending");
    1695             : 
    1696           0 :     } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
    1697           0 :         sort_return(-1, "call id");
    1698             : 
    1699           0 :     } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
    1700           0 :         sort_return(1, "call id");
    1701             : 
    1702           0 :     } else if (a_call_id >= 0 && b_call_id >= 0
    1703           0 :                && (!same_node || a_call_id == b_call_id)) {
    1704             :         /* The op and last_failed_op are the same. Order on
    1705             :          * PCMK_XA_LAST_RC_CHANGE.
    1706             :          */
    1707           0 :         time_t last_a = -1;
    1708           0 :         time_t last_b = -1;
    1709             : 
    1710           0 :         crm_element_value_epoch(xml_a, PCMK_XA_LAST_RC_CHANGE, &last_a);
    1711           0 :         crm_element_value_epoch(xml_b, PCMK_XA_LAST_RC_CHANGE, &last_b);
    1712             : 
    1713           0 :         crm_trace("rc-change: %lld vs %lld",
    1714             :                   (long long) last_a, (long long) last_b);
    1715           0 :         if (last_a >= 0 && last_a < last_b) {
    1716           0 :             sort_return(-1, "rc-change");
    1717             : 
    1718           0 :         } else if (last_b >= 0 && last_a > last_b) {
    1719           0 :             sort_return(1, "rc-change");
    1720             :         }
    1721           0 :         sort_return(0, "rc-change");
    1722             : 
    1723             :     } else {
    1724             :         /* One of the inputs is a pending operation.
    1725             :          * Attempt to use PCMK__XA_TRANSITION_MAGIC to determine its age relative
    1726             :          * to the other.
    1727             :          */
    1728             : 
    1729           0 :         int a_id = -1;
    1730           0 :         int b_id = -1;
    1731             : 
    1732           0 :         const char *a_magic = crm_element_value(xml_a,
    1733             :                                                 PCMK__XA_TRANSITION_MAGIC);
    1734           0 :         const char *b_magic = crm_element_value(xml_b,
    1735             :                                                 PCMK__XA_TRANSITION_MAGIC);
    1736             : 
    1737           0 :         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
    1738           0 :         if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
    1739             :                                      NULL)) {
    1740           0 :             sort_return(0, "bad magic a");
    1741             :         }
    1742           0 :         if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
    1743             :                                      NULL)) {
    1744           0 :             sort_return(0, "bad magic b");
    1745             :         }
    1746             :         /* try to determine the relative age of the operation...
    1747             :          * some pending operations (e.g. a start) may have been superseded
    1748             :          *   by a subsequent stop
    1749             :          *
    1750             :          * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
    1751             :          */
    1752           0 :         if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
    1753             :             /*
    1754             :              * some of the logic in here may be redundant...
    1755             :              *
    1756             :              * if the UUID from the TE doesn't match then one better
    1757             :              *   be a pending operation.
    1758             :              * pending operations don't survive between elections and joins
    1759             :              *   because we query the LRM directly
    1760             :              */
    1761             : 
    1762           0 :             if (b_call_id == -1) {
    1763           0 :                 sort_return(-1, "transition + call");
    1764             : 
    1765           0 :             } else if (a_call_id == -1) {
    1766           0 :                 sort_return(1, "transition + call");
    1767             :             }
    1768             : 
    1769           0 :         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
    1770           0 :             sort_return(-1, "transition");
    1771             : 
    1772           0 :         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
    1773           0 :             sort_return(1, "transition");
    1774             :         }
    1775             :     }
    1776             : 
    1777             :     /* we should never end up here */
    1778           0 :     CRM_CHECK(FALSE, sort_return(0, "default"));
    1779             : }
    1780             : 
    1781             : gint
    1782           0 : sort_op_by_callid(gconstpointer a, gconstpointer b)
    1783             : {
    1784           0 :     const xmlNode *xml_a = a;
    1785           0 :     const xmlNode *xml_b = b;
    1786             : 
    1787           0 :     return pe__is_newer_op(xml_a, xml_b, true);
    1788             : }
    1789             : 
    1790             : /*!
    1791             :  * \internal
    1792             :  * \brief Create a new pseudo-action for a resource
    1793             :  *
    1794             :  * \param[in,out] rsc       Resource to create action for
    1795             :  * \param[in]     task      Action name
    1796             :  * \param[in]     optional  Whether action should be considered optional
    1797             :  * \param[in]     runnable  Whethe action should be considered runnable
    1798             :  *
    1799             :  * \return New action object corresponding to arguments
    1800             :  */
    1801             : pcmk_action_t *
    1802           0 : pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
    1803             :                           bool runnable)
    1804             : {
    1805           0 :     pcmk_action_t *action = NULL;
    1806             : 
    1807           0 :     CRM_ASSERT((rsc != NULL) && (task != NULL));
    1808             : 
    1809           0 :     action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
    1810             :                            optional, rsc->cluster);
    1811           0 :     pcmk__set_action_flags(action, pcmk_action_pseudo);
    1812           0 :     if (runnable) {
    1813           0 :         pcmk__set_action_flags(action, pcmk_action_runnable);
    1814             :     }
    1815           0 :     return action;
    1816             : }
    1817             : 
    1818             : /*!
    1819             :  * \internal
    1820             :  * \brief Add the expected result to an action
    1821             :  *
    1822             :  * \param[in,out] action           Action to add expected result to
    1823             :  * \param[in]     expected_result  Expected result to add
    1824             :  *
    1825             :  * \note This is more efficient than calling pcmk__insert_meta().
    1826             :  */
    1827             : void
    1828           0 : pe__add_action_expected_result(pcmk_action_t *action, int expected_result)
    1829             : {
    1830           0 :     CRM_ASSERT((action != NULL) && (action->meta != NULL));
    1831             : 
    1832           0 :     g_hash_table_insert(action->meta, pcmk__str_copy(PCMK__META_OP_TARGET_RC),
    1833           0 :                         pcmk__itoa(expected_result));
    1834           0 : }

Generated by: LCOV version 1.14