LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_recurring.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 247 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 15 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 General Public License version 2
       7             :  * or later (GPLv2+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <stdbool.h>
      13             : 
      14             : #include <crm/common/xml.h>
      15             : #include <crm/common/scheduler_internal.h>
      16             : #include <pacemaker-internal.h>
      17             : 
      18             : #include "libpacemaker_private.h"
      19             : 
      20             : // Information parsed from an operation history entry in the CIB
      21             : struct op_history {
      22             :     // XML attributes
      23             :     const char *id;         // ID of history entry
      24             :     const char *name;       // Action name
      25             : 
      26             :     // Parsed information
      27             :     char *key;              // Operation key for action
      28             :     enum rsc_role_e role;   // Action role (or pcmk_role_unknown for default)
      29             :     guint interval_ms;      // Action interval
      30             : };
      31             : 
      32             : /*!
      33             :  * \internal
      34             :  * \brief Parse an interval from XML
      35             :  *
      36             :  * \param[in] xml  XML containing an interval attribute
      37             :  *
      38             :  * \return Interval parsed from XML (or 0 as default)
      39             :  */
      40             : static guint
      41           0 : xe_interval(const xmlNode *xml)
      42             : {
      43           0 :     guint interval_ms = 0U;
      44             : 
      45           0 :     pcmk_parse_interval_spec(crm_element_value(xml, PCMK_META_INTERVAL),
      46             :                              &interval_ms);
      47           0 :     return interval_ms;
      48             : }
      49             : 
      50             : /*!
      51             :  * \internal
      52             :  * \brief Check whether an operation exists multiple times in resource history
      53             :  *
      54             :  * \param[in] rsc          Resource with history to search
      55             :  * \param[in] name         Name of action to search for
      56             :  * \param[in] interval_ms  Interval (in milliseconds) of action to search for
      57             :  *
      58             :  * \return true if an operation with \p name and \p interval_ms exists more than
      59             :  *         once in the operation history of \p rsc, otherwise false
      60             :  */
      61             : static bool
      62           0 : is_op_dup(const pcmk_resource_t *rsc, const char *name, guint interval_ms)
      63             : {
      64           0 :     const char *id = NULL;
      65             : 
      66           0 :     for (xmlNode *op = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, NULL,
      67             :                                             NULL);
      68           0 :          op != NULL; op = pcmk__xe_next_same(op)) {
      69             : 
      70             :         // Check whether action name and interval match
      71           0 :         if (!pcmk__str_eq(crm_element_value(op, PCMK_XA_NAME), name,
      72             :                           pcmk__str_none)
      73           0 :             || (xe_interval(op) != interval_ms)) {
      74           0 :             continue;
      75             :         }
      76             : 
      77           0 :         if (pcmk__xe_id(op) == NULL) {
      78           0 :             continue; // Shouldn't be possible
      79             :         }
      80             : 
      81           0 :         if (id == NULL) {
      82           0 :             id = pcmk__xe_id(op); // First matching op
      83             :         } else {
      84           0 :             pcmk__config_err("Operation %s is duplicate of %s (do not use "
      85             :                              "same name and interval combination more "
      86             :                              "than once per resource)", pcmk__xe_id(op), id);
      87           0 :             return true;
      88             :         }
      89             :     }
      90           0 :     return false;
      91             : }
      92             : 
      93             : /*!
      94             :  * \internal
      95             :  * \brief Check whether an action name is one that can be recurring
      96             :  *
      97             :  * \param[in] name  Action name to check
      98             :  *
      99             :  * \return true if \p name is an action known to be unsuitable as a recurring
     100             :  *         operation, otherwise false
     101             :  *
     102             :  * \note Pacemaker's current philosophy is to allow users to configure recurring
     103             :  *       operations except for a short list of actions known not to be suitable
     104             :  *       for that (as opposed to allowing only actions known to be suitable,
     105             :  *       which includes only monitor). Among other things, this approach allows
     106             :  *       users to define their own custom operations and make them recurring,
     107             :  *       though that use case is not well tested.
     108             :  */
     109             : static bool
     110           0 : op_cannot_recur(const char *name)
     111             : {
     112           0 :     return pcmk__str_any_of(name, PCMK_ACTION_STOP, PCMK_ACTION_START,
     113             :                             PCMK_ACTION_DEMOTE, PCMK_ACTION_PROMOTE,
     114             :                             PCMK_ACTION_RELOAD_AGENT,
     115             :                             PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM,
     116             :                             NULL);
     117             : }
     118             : 
     119             : /*!
     120             :  * \internal
     121             :  * \brief Check whether a resource history entry is for a recurring action
     122             :  *
     123             :  * \param[in]  rsc          Resource that history entry is for
     124             :  * \param[in]  xml          XML of resource history entry to check
     125             :  * \param[out] op           Where to store parsed info if recurring
     126             :  *
     127             :  * \return true if \p xml is for a recurring action, otherwise false
     128             :  */
     129             : static bool
     130           0 : is_recurring_history(const pcmk_resource_t *rsc, const xmlNode *xml,
     131             :                      struct op_history *op)
     132             : {
     133           0 :     const char *role = NULL;
     134             : 
     135           0 :     op->interval_ms = xe_interval(xml);
     136           0 :     if (op->interval_ms == 0) {
     137           0 :         return false; // Not recurring
     138             :     }
     139             : 
     140           0 :     op->id = pcmk__xe_id(xml);
     141           0 :     if (pcmk__str_empty(op->id)) {
     142           0 :         pcmk__config_err("Ignoring resource history entry without ID");
     143           0 :         return false; // Shouldn't be possible (unless CIB was manually edited)
     144             :     }
     145             : 
     146           0 :     op->name = crm_element_value(xml, PCMK_XA_NAME);
     147           0 :     if (op_cannot_recur(op->name)) {
     148           0 :         pcmk__config_err("Ignoring %s because %s action cannot be recurring",
     149             :                          op->id, pcmk__s(op->name, "unnamed"));
     150           0 :         return false;
     151             :     }
     152             : 
     153             :     // There should only be one recurring operation per action/interval
     154           0 :     if (is_op_dup(rsc, op->name, op->interval_ms)) {
     155           0 :         return false;
     156             :     }
     157             : 
     158             :     // Ensure role is valid if specified
     159           0 :     role = crm_element_value(xml, PCMK_XA_ROLE);
     160           0 :     if (role == NULL) {
     161           0 :         op->role = pcmk_role_unknown;
     162             :     } else {
     163           0 :         op->role = pcmk_parse_role(role);
     164           0 :         if (op->role == pcmk_role_unknown) {
     165           0 :             pcmk__config_err("Ignoring %s role because %s is not a valid role",
     166             :                              op->id, role);
     167           0 :             return false;
     168             :         }
     169             :     }
     170             : 
     171             :     // Only actions that are still configured and enabled matter
     172           0 :     if (pcmk__find_action_config(rsc, op->name, op->interval_ms,
     173             :                                  false) == NULL) {
     174           0 :         pcmk__rsc_trace(rsc,
     175             :                         "Ignoring %s (%s-interval %s for %s) because it is "
     176             :                         "disabled or no longer in configuration",
     177             :                         op->id, pcmk__readable_interval(op->interval_ms),
     178             :                         op->name, rsc->id);
     179           0 :         return false;
     180             :     }
     181             : 
     182           0 :     op->key = pcmk__op_key(rsc->id, op->name, op->interval_ms);
     183           0 :     return true;
     184             : }
     185             : 
     186             : /*!
     187             :  * \internal
     188             :  * \brief Check whether a recurring action for an active role should be optional
     189             :  *
     190             :  * \param[in]     rsc    Resource that recurring action is for
     191             :  * \param[in]     node   Node that \p rsc will be active on (if any)
     192             :  * \param[in]     key    Operation key for recurring action to check
     193             :  * \param[in,out] start  Start action for \p rsc
     194             :  *
     195             :  * \return true if recurring action should be optional, otherwise false
     196             :  */
     197             : static bool
     198           0 : active_recurring_should_be_optional(const pcmk_resource_t *rsc,
     199             :                                     const pcmk_node_t *node, const char *key,
     200             :                                     pcmk_action_t *start)
     201             : {
     202           0 :     GList *possible_matches = NULL;
     203             : 
     204           0 :     if (node == NULL) { // Should only be possible if unmanaged and stopped
     205           0 :         pcmk__rsc_trace(rsc,
     206             :                         "%s will be mandatory because resource is unmanaged",
     207             :                         key);
     208           0 :         return false;
     209             :     }
     210             : 
     211           0 :     if (!pcmk_is_set(rsc->cmds->action_flags(start, NULL),
     212             :                      pcmk_action_optional)) {
     213           0 :         pcmk__rsc_trace(rsc, "%s will be mandatory because %s is",
     214             :                         key, start->uuid);
     215           0 :         return false;
     216             :     }
     217             : 
     218           0 :     possible_matches = find_actions_exact(rsc->actions, key, node);
     219           0 :     if (possible_matches == NULL) {
     220           0 :         pcmk__rsc_trace(rsc,
     221             :                         "%s will be mandatory because it is not active on %s",
     222             :                         key, pcmk__node_name(node));
     223           0 :         return false;
     224             :     }
     225             : 
     226           0 :     for (const GList *iter = possible_matches;
     227           0 :          iter != NULL; iter = iter->next) {
     228             : 
     229           0 :         const pcmk_action_t *op = (const pcmk_action_t *) iter->data;
     230             : 
     231           0 :         if (pcmk_is_set(op->flags, pcmk_action_reschedule)) {
     232           0 :             pcmk__rsc_trace(rsc,
     233             :                             "%s will be mandatory because "
     234             :                             "it needs to be rescheduled", key);
     235           0 :             g_list_free(possible_matches);
     236           0 :             return false;
     237             :         }
     238             :     }
     239             : 
     240           0 :     g_list_free(possible_matches);
     241           0 :     return true;
     242             : }
     243             : 
     244             : /*!
     245             :  * \internal
     246             :  * \brief Create recurring action from resource history entry for an active role
     247             :  *
     248             :  * \param[in,out] rsc    Resource that resource history is for
     249             :  * \param[in,out] start  Start action for \p rsc on \p node
     250             :  * \param[in]     node   Node that resource will be active on (if any)
     251             :  * \param[in]     op     Resource history entry
     252             :  */
     253             : static void
     254           0 : recurring_op_for_active(pcmk_resource_t *rsc, pcmk_action_t *start,
     255             :                         const pcmk_node_t *node, const struct op_history *op)
     256             : {
     257           0 :     pcmk_action_t *mon = NULL;
     258           0 :     bool is_optional = true;
     259           0 :     bool role_match = false;
     260           0 :     enum rsc_role_e monitor_role = op->role;
     261             : 
     262             :     // We're only interested in recurring actions for active roles
     263           0 :     if (monitor_role == pcmk_role_stopped) {
     264           0 :         return;
     265             :     }
     266             : 
     267           0 :     is_optional = active_recurring_should_be_optional(rsc, node, op->key,
     268             :                                                       start);
     269             : 
     270             :     // Check whether monitor's role matches role resource will have
     271           0 :     if (monitor_role == pcmk_role_unknown) {
     272           0 :         monitor_role = pcmk_role_unpromoted;
     273           0 :         role_match = (rsc->next_role != pcmk_role_promoted);
     274             :     } else {
     275           0 :         role_match = (rsc->next_role == monitor_role);
     276             :     }
     277             : 
     278           0 :     if (!role_match) {
     279           0 :         if (is_optional) { // It's running, so cancel it
     280           0 :             char *after_key = NULL;
     281           0 :             pcmk_action_t *cancel_op = pcmk__new_cancel_action(rsc, op->name,
     282           0 :                                                                op->interval_ms,
     283             :                                                                node);
     284             : 
     285           0 :             switch (rsc->role) {
     286           0 :                 case pcmk_role_unpromoted:
     287             :                 case pcmk_role_started:
     288           0 :                     if (rsc->next_role == pcmk_role_promoted) {
     289           0 :                         after_key = promote_key(rsc);
     290             : 
     291           0 :                     } else if (rsc->next_role == pcmk_role_stopped) {
     292           0 :                         after_key = stop_key(rsc);
     293             :                     }
     294             : 
     295           0 :                     break;
     296           0 :                 case pcmk_role_promoted:
     297           0 :                     after_key = demote_key(rsc);
     298           0 :                     break;
     299           0 :                 default:
     300           0 :                     break;
     301             :             }
     302             : 
     303           0 :             if (after_key) {
     304           0 :                 pcmk__new_ordering(rsc, NULL, cancel_op, rsc, after_key, NULL,
     305             :                                    pcmk__ar_unrunnable_first_blocks,
     306             :                                    rsc->cluster);
     307             :             }
     308             :         }
     309             : 
     310           0 :         do_crm_log((is_optional? LOG_INFO : LOG_TRACE),
     311             :                    "%s recurring action %s because %s configured for %s role "
     312             :                    "(not %s)",
     313             :                    (is_optional? "Cancelling" : "Ignoring"), op->key, op->id,
     314             :                    pcmk_role_text(monitor_role),
     315             :                    pcmk_role_text(rsc->next_role));
     316           0 :         return;
     317             :     }
     318             : 
     319           0 :     pcmk__rsc_trace(rsc,
     320             :                     "Creating %s recurring action %s for %s (%s %s on %s)",
     321             :                     (is_optional? "optional" : "mandatory"), op->key,
     322             :                     op->id, rsc->id, pcmk_role_text(rsc->next_role),
     323             :                     pcmk__node_name(node));
     324             : 
     325           0 :     mon = custom_action(rsc, strdup(op->key), op->name, node, is_optional,
     326             :                         rsc->cluster);
     327             : 
     328           0 :     if (!pcmk_is_set(start->flags, pcmk_action_runnable)) {
     329           0 :         pcmk__rsc_trace(rsc, "%s is unrunnable because start is", mon->uuid);
     330           0 :         pcmk__clear_action_flags(mon, pcmk_action_runnable);
     331             : 
     332           0 :     } else if ((node == NULL) || !node->details->online
     333           0 :                || node->details->unclean) {
     334           0 :         pcmk__rsc_trace(rsc, "%s is unrunnable because no node is available",
     335             :                         mon->uuid);
     336           0 :         pcmk__clear_action_flags(mon, pcmk_action_runnable);
     337             : 
     338           0 :     } else if (!pcmk_is_set(mon->flags, pcmk_action_optional)) {
     339           0 :         pcmk__rsc_info(rsc, "Start %s-interval %s for %s on %s",
     340             :                        pcmk__readable_interval(op->interval_ms), mon->task,
     341             :                        rsc->id, pcmk__node_name(node));
     342             :     }
     343             : 
     344           0 :     if (rsc->next_role == pcmk_role_promoted) {
     345           0 :         pe__add_action_expected_result(mon, CRM_EX_PROMOTED);
     346             :     }
     347             : 
     348             :     // Order monitor relative to other actions
     349           0 :     if ((node == NULL) || pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     350           0 :         pcmk__new_ordering(rsc, start_key(rsc), NULL,
     351           0 :                            NULL, strdup(mon->uuid), mon,
     352             :                            pcmk__ar_first_implies_then
     353             :                            |pcmk__ar_unrunnable_first_blocks,
     354             :                            rsc->cluster);
     355             : 
     356           0 :         pcmk__new_ordering(rsc, reload_key(rsc), NULL,
     357           0 :                            NULL, strdup(mon->uuid), mon,
     358             :                            pcmk__ar_first_implies_then
     359             :                            |pcmk__ar_unrunnable_first_blocks,
     360             :                            rsc->cluster);
     361             : 
     362           0 :         if (rsc->next_role == pcmk_role_promoted) {
     363           0 :             pcmk__new_ordering(rsc, promote_key(rsc), NULL,
     364             :                                rsc, NULL, mon,
     365             :                                pcmk__ar_ordered
     366             :                                |pcmk__ar_unrunnable_first_blocks,
     367             :                                rsc->cluster);
     368             : 
     369           0 :         } else if (rsc->role == pcmk_role_promoted) {
     370           0 :             pcmk__new_ordering(rsc, demote_key(rsc), NULL,
     371             :                                rsc, NULL, mon,
     372             :                                pcmk__ar_ordered
     373             :                                |pcmk__ar_unrunnable_first_blocks,
     374             :                                rsc->cluster);
     375             :         }
     376             :     }
     377             : }
     378             : 
     379             : /*!
     380             :  * \internal
     381             :  * \brief Cancel a recurring action if running on a node
     382             :  *
     383             :  * \param[in,out] rsc          Resource that action is for
     384             :  * \param[in]     node         Node to cancel action on
     385             :  * \param[in]     key          Operation key for action
     386             :  * \param[in]     name         Action name
     387             :  * \param[in]     interval_ms  Action interval (in milliseconds)
     388             :  */
     389             : static void
     390           0 : cancel_if_running(pcmk_resource_t *rsc, const pcmk_node_t *node,
     391             :                   const char *key, const char *name, guint interval_ms)
     392             : {
     393           0 :     GList *possible_matches = find_actions_exact(rsc->actions, key, node);
     394           0 :     pcmk_action_t *cancel_op = NULL;
     395             : 
     396           0 :     if (possible_matches == NULL) {
     397           0 :         return; // Recurring action isn't running on this node
     398             :     }
     399           0 :     g_list_free(possible_matches);
     400             : 
     401           0 :     cancel_op = pcmk__new_cancel_action(rsc, name, interval_ms, node);
     402             : 
     403           0 :     switch (rsc->next_role) {
     404           0 :         case pcmk_role_started:
     405             :         case pcmk_role_unpromoted:
     406             :             /* Order starts after cancel. If the current role is
     407             :              * stopped, this cancels the monitor before the resource
     408             :              * starts; if the current role is started, then this cancels
     409             :              * the monitor on a migration target before starting there.
     410             :              */
     411           0 :             pcmk__new_ordering(rsc, NULL, cancel_op,
     412           0 :                                rsc, start_key(rsc), NULL,
     413             :                                pcmk__ar_unrunnable_first_blocks, rsc->cluster);
     414           0 :             break;
     415           0 :         default:
     416           0 :             break;
     417             :     }
     418           0 :     pcmk__rsc_info(rsc,
     419             :                    "Cancelling %s-interval %s action for %s on %s because "
     420             :                    "configured for " PCMK_ROLE_STOPPED " role (not %s)",
     421             :                    pcmk__readable_interval(interval_ms), name, rsc->id,
     422             :                    pcmk__node_name(node), pcmk_role_text(rsc->next_role));
     423             : }
     424             : 
     425             : /*!
     426             :  * \internal
     427             :  * \brief Order an action after all probes of a resource on a node
     428             :  *
     429             :  * \param[in,out] rsc     Resource to check for probes
     430             :  * \param[in]     node    Node to check for probes of \p rsc
     431             :  * \param[in,out] action  Action to order after probes of \p rsc on \p node
     432             :  */
     433             : static void
     434           0 : order_after_probes(pcmk_resource_t *rsc, const pcmk_node_t *node,
     435             :                    pcmk_action_t *action)
     436             : {
     437           0 :     GList *probes = pe__resource_actions(rsc, node, PCMK_ACTION_MONITOR, FALSE);
     438             : 
     439           0 :     for (GList *iter = probes; iter != NULL; iter = iter->next) {
     440           0 :         order_actions((pcmk_action_t *) iter->data, action,
     441             :                       pcmk__ar_unrunnable_first_blocks);
     442             :     }
     443           0 :     g_list_free(probes);
     444           0 : }
     445             : 
     446             : /*!
     447             :  * \internal
     448             :  * \brief Order an action after all stops of a resource on a node
     449             :  *
     450             :  * \param[in,out] rsc     Resource to check for stops
     451             :  * \param[in]     node    Node to check for stops of \p rsc
     452             :  * \param[in,out] action  Action to order after stops of \p rsc on \p node
     453             :  */
     454             : static void
     455           0 : order_after_stops(pcmk_resource_t *rsc, const pcmk_node_t *node,
     456             :                   pcmk_action_t *action)
     457             : {
     458           0 :     GList *stop_ops = pe__resource_actions(rsc, node, PCMK_ACTION_STOP, TRUE);
     459             : 
     460           0 :     for (GList *iter = stop_ops; iter != NULL; iter = iter->next) {
     461           0 :         pcmk_action_t *stop = (pcmk_action_t *) iter->data;
     462             : 
     463           0 :         if (!pcmk_is_set(stop->flags, pcmk_action_optional)
     464           0 :             && !pcmk_is_set(action->flags, pcmk_action_optional)
     465           0 :             && !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     466           0 :             pcmk__rsc_trace(rsc, "%s optional on %s: unmanaged",
     467             :                             action->uuid, pcmk__node_name(node));
     468           0 :             pcmk__set_action_flags(action, pcmk_action_optional);
     469             :         }
     470             : 
     471           0 :         if (!pcmk_is_set(stop->flags, pcmk_action_runnable)) {
     472           0 :             crm_debug("%s unrunnable on %s: stop is unrunnable",
     473             :                       action->uuid, pcmk__node_name(node));
     474           0 :             pcmk__clear_action_flags(action, pcmk_action_runnable);
     475             :         }
     476             : 
     477           0 :         if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     478           0 :             pcmk__new_ordering(rsc, stop_key(rsc), stop,
     479             :                                NULL, NULL, action,
     480             :                                pcmk__ar_first_implies_then
     481             :                                |pcmk__ar_unrunnable_first_blocks,
     482             :                                rsc->cluster);
     483             :         }
     484             :     }
     485           0 :     g_list_free(stop_ops);
     486           0 : }
     487             : 
     488             : /*!
     489             :  * \internal
     490             :  * \brief Create recurring action from resource history entry for inactive role
     491             :  *
     492             :  * \param[in,out] rsc    Resource that resource history is for
     493             :  * \param[in]     node   Node that resource will be active on (if any)
     494             :  * \param[in]     op     Resource history entry
     495             :  */
     496             : static void
     497           0 : recurring_op_for_inactive(pcmk_resource_t *rsc, const pcmk_node_t *node,
     498             :                           const struct op_history *op)
     499             : {
     500           0 :     GList *possible_matches = NULL;
     501             : 
     502             :     // We're only interested in recurring actions for the inactive role
     503           0 :     if (op->role != pcmk_role_stopped) {
     504           0 :         return;
     505             :     }
     506             : 
     507           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
     508           0 :         crm_notice("Ignoring %s (recurring monitors for " PCMK_ROLE_STOPPED
     509             :                    " role are not supported for anonymous clones)", op->id);
     510           0 :         return; // @TODO add support
     511             :     }
     512             : 
     513           0 :     pcmk__rsc_trace(rsc,
     514             :                     "Creating recurring action %s for %s on nodes "
     515             :                     "where it should not be running", op->id, rsc->id);
     516             : 
     517           0 :     for (GList *iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) {
     518           0 :         pcmk_node_t *stop_node = (pcmk_node_t *) iter->data;
     519             : 
     520           0 :         bool is_optional = true;
     521           0 :         pcmk_action_t *stopped_mon = NULL;
     522             : 
     523             :         // Cancel action on node where resource will be active
     524           0 :         if ((node != NULL)
     525           0 :             && pcmk__str_eq(stop_node->details->uname, node->details->uname,
     526             :                             pcmk__str_casei)) {
     527           0 :             cancel_if_running(rsc, node, op->key, op->name, op->interval_ms);
     528           0 :             continue;
     529             :         }
     530             : 
     531             :         // Recurring action on this node is optional if it's already active here
     532           0 :         possible_matches = find_actions_exact(rsc->actions, op->key, stop_node);
     533           0 :         is_optional = (possible_matches != NULL);
     534           0 :         g_list_free(possible_matches);
     535             : 
     536           0 :         pcmk__rsc_trace(rsc,
     537             :                         "Creating %s recurring action %s for %s (%s "
     538             :                         PCMK_ROLE_STOPPED " on %s)",
     539             :                         (is_optional? "optional" : "mandatory"),
     540             :                         op->key, op->id, rsc->id, pcmk__node_name(stop_node));
     541             : 
     542           0 :         stopped_mon = custom_action(rsc, strdup(op->key), op->name, stop_node,
     543             :                                     is_optional, rsc->cluster);
     544             : 
     545           0 :         pe__add_action_expected_result(stopped_mon, CRM_EX_NOT_RUNNING);
     546             : 
     547           0 :         if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     548           0 :             order_after_probes(rsc, stop_node, stopped_mon);
     549             :         }
     550             : 
     551             :         /* The recurring action is for the inactive role, so it shouldn't be
     552             :          * performed until the resource is inactive.
     553             :          */
     554           0 :         order_after_stops(rsc, stop_node, stopped_mon);
     555             : 
     556           0 :         if (!stop_node->details->online || stop_node->details->unclean) {
     557           0 :             pcmk__rsc_debug(rsc, "%s unrunnable on %s: node unavailable)",
     558             :                             stopped_mon->uuid, pcmk__node_name(stop_node));
     559           0 :             pcmk__clear_action_flags(stopped_mon, pcmk_action_runnable);
     560             :         }
     561             : 
     562           0 :         if (pcmk_is_set(stopped_mon->flags, pcmk_action_runnable)
     563           0 :             && !pcmk_is_set(stopped_mon->flags, pcmk_action_optional)) {
     564           0 :             crm_notice("Start recurring %s-interval %s for "
     565             :                        PCMK_ROLE_STOPPED " %s on %s",
     566             :                        pcmk__readable_interval(op->interval_ms),
     567             :                        stopped_mon->task, rsc->id, pcmk__node_name(stop_node));
     568             :         }
     569             :     }
     570             : }
     571             : 
     572             : /*!
     573             :  * \internal
     574             :  * \brief Create recurring actions for a resource
     575             :  *
     576             :  * \param[in,out] rsc  Resource to create recurring actions for
     577             :  */
     578             : void
     579           0 : pcmk__create_recurring_actions(pcmk_resource_t *rsc)
     580             : {
     581           0 :     pcmk_action_t *start = NULL;
     582             : 
     583           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) {
     584           0 :         pcmk__rsc_trace(rsc,
     585             :                         "Skipping recurring actions for blocked resource %s",
     586             :                         rsc->id);
     587           0 :         return;
     588             :     }
     589             : 
     590           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
     591           0 :         pcmk__rsc_trace(rsc,
     592             :                         "Skipping recurring actions for %s "
     593             :                         "in maintenance mode", rsc->id);
     594           0 :         return;
     595             :     }
     596             : 
     597           0 :     if (rsc->allocated_to == NULL) {
     598             :         // Recurring actions for active roles not needed
     599             : 
     600           0 :     } else if (rsc->allocated_to->details->maintenance) {
     601           0 :         pcmk__rsc_trace(rsc,
     602             :                         "Skipping recurring actions for %s on %s "
     603             :                         "in maintenance mode",
     604             :                         rsc->id, pcmk__node_name(rsc->allocated_to));
     605             : 
     606           0 :     } else if ((rsc->next_role != pcmk_role_stopped)
     607           0 :         || !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     608             :         // Recurring actions for active roles needed
     609           0 :         start = start_action(rsc, rsc->allocated_to, TRUE);
     610             :     }
     611             : 
     612           0 :     pcmk__rsc_trace(rsc, "Creating any recurring actions needed for %s",
     613             :                     rsc->id);
     614             : 
     615           0 :     for (xmlNode *op = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, NULL,
     616             :                                             NULL);
     617           0 :          op != NULL; op = pcmk__xe_next_same(op)) {
     618             : 
     619           0 :         struct op_history op_history = { NULL, };
     620             : 
     621           0 :         if (!is_recurring_history(rsc, op, &op_history)) {
     622           0 :             continue;
     623             :         }
     624             : 
     625           0 :         if (start != NULL) {
     626           0 :             recurring_op_for_active(rsc, start, rsc->allocated_to, &op_history);
     627             :         }
     628           0 :         recurring_op_for_inactive(rsc, rsc->allocated_to, &op_history);
     629             : 
     630           0 :         free(op_history.key);
     631             :     }
     632             : }
     633             : 
     634             : /*!
     635             :  * \internal
     636             :  * \brief Create an executor cancel action
     637             :  *
     638             :  * \param[in,out] rsc          Resource of action to cancel
     639             :  * \param[in]     task         Name of action to cancel
     640             :  * \param[in]     interval_ms  Interval of action to cancel
     641             :  * \param[in]     node         Node of action to cancel
     642             :  *
     643             :  * \return Created op
     644             :  */
     645             : pcmk_action_t *
     646           0 : pcmk__new_cancel_action(pcmk_resource_t *rsc, const char *task,
     647             :                         guint interval_ms, const pcmk_node_t *node)
     648             : {
     649           0 :     pcmk_action_t *cancel_op = NULL;
     650           0 :     char *key = NULL;
     651           0 :     char *interval_ms_s = NULL;
     652             : 
     653           0 :     CRM_ASSERT((rsc != NULL) && (task != NULL) && (node != NULL));
     654             : 
     655             :     // @TODO dangerous if possible to schedule another action with this key
     656           0 :     key = pcmk__op_key(rsc->id, task, interval_ms);
     657             : 
     658           0 :     cancel_op = custom_action(rsc, key, PCMK_ACTION_CANCEL, node, FALSE,
     659             :                               rsc->cluster);
     660             : 
     661           0 :     cancel_op->task = pcmk__str_copy(PCMK_ACTION_CANCEL);
     662           0 :     cancel_op->cancel_task = pcmk__str_copy(task);
     663             : 
     664           0 :     interval_ms_s = crm_strdup_printf("%u", interval_ms);
     665           0 :     pcmk__insert_meta(cancel_op, PCMK_XA_OPERATION, task);
     666           0 :     pcmk__insert_meta(cancel_op, PCMK_META_INTERVAL, interval_ms_s);
     667           0 :     free(interval_ms_s);
     668             : 
     669           0 :     return cancel_op;
     670             : }
     671             : 
     672             : /*!
     673             :  * \internal
     674             :  * \brief Schedule cancellation of a recurring action
     675             :  *
     676             :  * \param[in,out] rsc          Resource that action is for
     677             :  * \param[in]     call_id      Action's call ID from history
     678             :  * \param[in]     task         Action name
     679             :  * \param[in]     interval_ms  Action interval
     680             :  * \param[in]     node         Node that history entry is for
     681             :  * \param[in]     reason       Short description of why action is cancelled
     682             :  */
     683             : void
     684           0 : pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id,
     685             :                       const char *task, guint interval_ms,
     686             :                       const pcmk_node_t *node, const char *reason)
     687             : {
     688           0 :     pcmk_action_t *cancel = NULL;
     689             : 
     690           0 :     CRM_CHECK((rsc != NULL) && (task != NULL)
     691             :               && (node != NULL) && (reason != NULL),
     692             :               return);
     693             : 
     694           0 :     crm_info("Recurring %s-interval %s for %s will be stopped on %s: %s",
     695             :              pcmk__readable_interval(interval_ms), task, rsc->id,
     696             :              pcmk__node_name(node), reason);
     697           0 :     cancel = pcmk__new_cancel_action(rsc, task, interval_ms, node);
     698           0 :     pcmk__insert_meta(cancel, PCMK__XA_CALL_ID, call_id);
     699             : 
     700             :     // Cancellations happen after stops
     701           0 :     pcmk__new_ordering(rsc, stop_key(rsc), NULL, rsc, NULL, cancel,
     702             :                        pcmk__ar_ordered, rsc->cluster);
     703             : }
     704             : 
     705             : /*!
     706             :  * \internal
     707             :  * \brief Create a recurring action marked as needing rescheduling if active
     708             :  *
     709             :  * \param[in,out] rsc          Resource that action is for
     710             :  * \param[in]     task         Name of action being rescheduled
     711             :  * \param[in]     interval_ms  Action interval (in milliseconds)
     712             :  * \param[in,out] node         Node where action should be rescheduled
     713             :  */
     714             : void
     715           0 : pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task,
     716             :                            guint interval_ms, pcmk_node_t *node)
     717             : {
     718           0 :     pcmk_action_t *op = NULL;
     719             : 
     720           0 :     trigger_unfencing(rsc, node, "Device parameters changed (reschedule)",
     721             :                       NULL, rsc->cluster);
     722           0 :     op = custom_action(rsc, pcmk__op_key(rsc->id, task, interval_ms),
     723             :                        task, node, TRUE, rsc->cluster);
     724           0 :     pcmk__set_action_flags(op, pcmk_action_reschedule);
     725           0 : }
     726             : 
     727             : /*!
     728             :  * \internal
     729             :  * \brief Check whether an action is recurring
     730             :  *
     731             :  * \param[in] action  Action to check
     732             :  *
     733             :  * \return true if \p action has a nonzero interval, otherwise false
     734             :  */
     735             : bool
     736           0 : pcmk__action_is_recurring(const pcmk_action_t *action)
     737             : {
     738           0 :     guint interval_ms = 0;
     739             : 
     740           0 :     if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
     741             :                               &interval_ms) != pcmk_rc_ok) {
     742           0 :         return false;
     743             :     }
     744           0 :     return (interval_ms > 0);
     745             : }

Generated by: LCOV version 1.14