root/lib/pacemaker/pcmk_sched_recurring.c

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

DEFINITIONS

This source file includes following definitions.
  1. xe_interval
  2. is_op_dup
  3. op_cannot_recur
  4. is_recurring_history
  5. active_recurring_should_be_optional
  6. recurring_op_for_active
  7. cancel_if_running
  8. order_after_probes
  9. order_after_stops
  10. recurring_op_for_inactive
  11. pcmk__create_recurring_actions
  12. pcmk__new_cancel_action
  13. pcmk__schedule_cancel
  14. pcmk__reschedule_recurring
  15. pcmk__action_is_recurring

   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 xe_interval(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  42 {
  43     guint interval_ms = 0U;
  44 
  45     pcmk_parse_interval_spec(crm_element_value(xml, PCMK_META_INTERVAL),
  46                              &interval_ms);
  47     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 is_op_dup(const pcmk_resource_t *rsc, const char *name, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     const char *id = NULL;
  65 
  66     for (xmlNode *op = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, NULL,
  67                                             NULL);
  68          op != NULL; op = pcmk__xe_next_same(op)) {
  69 
  70         // Check whether action name and interval match
  71         if (!pcmk__str_eq(crm_element_value(op, PCMK_XA_NAME), name,
  72                           pcmk__str_none)
  73             || (xe_interval(op) != interval_ms)) {
  74             continue;
  75         }
  76 
  77         if (pcmk__xe_id(op) == NULL) {
  78             continue; // Shouldn't be possible
  79         }
  80 
  81         if (id == NULL) {
  82             id = pcmk__xe_id(op); // First matching op
  83         } else {
  84             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             return true;
  88         }
  89     }
  90     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 op_cannot_recur(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 111 {
 112     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 is_recurring_history(const pcmk_resource_t *rsc, const xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 131                      struct op_history *op)
 132 {
 133     const char *role = NULL;
 134 
 135     op->interval_ms = xe_interval(xml);
 136     if (op->interval_ms == 0) {
 137         return false; // Not recurring
 138     }
 139 
 140     op->id = pcmk__xe_id(xml);
 141     if (pcmk__str_empty(op->id)) {
 142         pcmk__config_err("Ignoring resource history entry without ID");
 143         return false; // Shouldn't be possible (unless CIB was manually edited)
 144     }
 145 
 146     op->name = crm_element_value(xml, PCMK_XA_NAME);
 147     if (op_cannot_recur(op->name)) {
 148         pcmk__config_err("Ignoring %s because %s action cannot be recurring",
 149                          op->id, pcmk__s(op->name, "unnamed"));
 150         return false;
 151     }
 152 
 153     // There should only be one recurring operation per action/interval
 154     if (is_op_dup(rsc, op->name, op->interval_ms)) {
 155         return false;
 156     }
 157 
 158     // Ensure role is valid if specified
 159     role = crm_element_value(xml, PCMK_XA_ROLE);
 160     if (role == NULL) {
 161         op->role = pcmk_role_unknown;
 162     } else {
 163         op->role = pcmk_parse_role(role);
 164         if (op->role == pcmk_role_unknown) {
 165             pcmk__config_err("Ignoring %s role because %s is not a valid role",
 166                              op->id, role);
 167             return false;
 168         }
 169     }
 170 
 171     // Only actions that are still configured and enabled matter
 172     if (pcmk__find_action_config(rsc, op->name, op->interval_ms,
 173                                  false) == NULL) {
 174         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         return false;
 180     }
 181 
 182     op->key = pcmk__op_key(rsc->id, op->name, op->interval_ms);
 183     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 active_recurring_should_be_optional(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 199                                     const pcmk_node_t *node, const char *key,
 200                                     pcmk_action_t *start)
 201 {
 202     GList *possible_matches = NULL;
 203 
 204     if (node == NULL) { // Should only be possible if unmanaged and stopped
 205         pcmk__rsc_trace(rsc,
 206                         "%s will be mandatory because resource is unmanaged",
 207                         key);
 208         return false;
 209     }
 210 
 211     if (!pcmk_is_set(rsc->cmds->action_flags(start, NULL),
 212                      pcmk_action_optional)) {
 213         pcmk__rsc_trace(rsc, "%s will be mandatory because %s is",
 214                         key, start->uuid);
 215         return false;
 216     }
 217 
 218     possible_matches = find_actions_exact(rsc->actions, key, node);
 219     if (possible_matches == NULL) {
 220         pcmk__rsc_trace(rsc,
 221                         "%s will be mandatory because it is not active on %s",
 222                         key, pcmk__node_name(node));
 223         return false;
 224     }
 225 
 226     for (const GList *iter = possible_matches;
 227          iter != NULL; iter = iter->next) {
 228 
 229         const pcmk_action_t *op = (const pcmk_action_t *) iter->data;
 230 
 231         if (pcmk_is_set(op->flags, pcmk_action_reschedule)) {
 232             pcmk__rsc_trace(rsc,
 233                             "%s will be mandatory because "
 234                             "it needs to be rescheduled", key);
 235             g_list_free(possible_matches);
 236             return false;
 237         }
 238     }
 239 
 240     g_list_free(possible_matches);
 241     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 recurring_op_for_active(pcmk_resource_t *rsc, pcmk_action_t *start,
     /* [previous][next][first][last][top][bottom][index][help] */
 255                         const pcmk_node_t *node, const struct op_history *op)
 256 {
 257     pcmk_action_t *mon = NULL;
 258     bool is_optional = true;
 259     bool role_match = false;
 260     enum rsc_role_e monitor_role = op->role;
 261 
 262     // We're only interested in recurring actions for active roles
 263     if (monitor_role == pcmk_role_stopped) {
 264         return;
 265     }
 266 
 267     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     if (monitor_role == pcmk_role_unknown) {
 272         monitor_role = pcmk_role_unpromoted;
 273         role_match = (rsc->next_role != pcmk_role_promoted);
 274     } else {
 275         role_match = (rsc->next_role == monitor_role);
 276     }
 277 
 278     if (!role_match) {
 279         if (is_optional) { // It's running, so cancel it
 280             char *after_key = NULL;
 281             pcmk_action_t *cancel_op = pcmk__new_cancel_action(rsc, op->name,
 282                                                                op->interval_ms,
 283                                                                node);
 284 
 285             switch (rsc->role) {
 286                 case pcmk_role_unpromoted:
 287                 case pcmk_role_started:
 288                     if (rsc->next_role == pcmk_role_promoted) {
 289                         after_key = promote_key(rsc);
 290 
 291                     } else if (rsc->next_role == pcmk_role_stopped) {
 292                         after_key = stop_key(rsc);
 293                     }
 294 
 295                     break;
 296                 case pcmk_role_promoted:
 297                     after_key = demote_key(rsc);
 298                     break;
 299                 default:
 300                     break;
 301             }
 302 
 303             if (after_key) {
 304                 pcmk__new_ordering(rsc, NULL, cancel_op, rsc, after_key, NULL,
 305                                    pcmk__ar_unrunnable_first_blocks,
 306                                    rsc->cluster);
 307             }
 308         }
 309 
 310         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         return;
 317     }
 318 
 319     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     mon = custom_action(rsc, strdup(op->key), op->name, node, is_optional,
 326                         rsc->cluster);
 327 
 328     if (!pcmk_is_set(start->flags, pcmk_action_runnable)) {
 329         pcmk__rsc_trace(rsc, "%s is unrunnable because start is", mon->uuid);
 330         pcmk__clear_action_flags(mon, pcmk_action_runnable);
 331 
 332     } else if ((node == NULL) || !node->details->online
 333                || node->details->unclean) {
 334         pcmk__rsc_trace(rsc, "%s is unrunnable because no node is available",
 335                         mon->uuid);
 336         pcmk__clear_action_flags(mon, pcmk_action_runnable);
 337 
 338     } else if (!pcmk_is_set(mon->flags, pcmk_action_optional)) {
 339         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     if (rsc->next_role == pcmk_role_promoted) {
 345         pe__add_action_expected_result(mon, CRM_EX_PROMOTED);
 346     }
 347 
 348     // Order monitor relative to other actions
 349     if ((node == NULL) || pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 350         pcmk__new_ordering(rsc, start_key(rsc), NULL,
 351                            NULL, strdup(mon->uuid), mon,
 352                            pcmk__ar_first_implies_then
 353                            |pcmk__ar_unrunnable_first_blocks,
 354                            rsc->cluster);
 355 
 356         pcmk__new_ordering(rsc, reload_key(rsc), NULL,
 357                            NULL, strdup(mon->uuid), mon,
 358                            pcmk__ar_first_implies_then
 359                            |pcmk__ar_unrunnable_first_blocks,
 360                            rsc->cluster);
 361 
 362         if (rsc->next_role == pcmk_role_promoted) {
 363             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         } else if (rsc->role == pcmk_role_promoted) {
 370             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 cancel_if_running(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 391                   const char *key, const char *name, guint interval_ms)
 392 {
 393     GList *possible_matches = find_actions_exact(rsc->actions, key, node);
 394     pcmk_action_t *cancel_op = NULL;
 395 
 396     if (possible_matches == NULL) {
 397         return; // Recurring action isn't running on this node
 398     }
 399     g_list_free(possible_matches);
 400 
 401     cancel_op = pcmk__new_cancel_action(rsc, name, interval_ms, node);
 402 
 403     switch (rsc->next_role) {
 404         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             pcmk__new_ordering(rsc, NULL, cancel_op,
 412                                rsc, start_key(rsc), NULL,
 413                                pcmk__ar_unrunnable_first_blocks, rsc->cluster);
 414             break;
 415         default:
 416             break;
 417     }
 418     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 order_after_probes(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 435                    pcmk_action_t *action)
 436 {
 437     GList *probes = pe__resource_actions(rsc, node, PCMK_ACTION_MONITOR, FALSE);
 438 
 439     for (GList *iter = probes; iter != NULL; iter = iter->next) {
 440         order_actions((pcmk_action_t *) iter->data, action,
 441                       pcmk__ar_unrunnable_first_blocks);
 442     }
 443     g_list_free(probes);
 444 }
 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 order_after_stops(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 456                   pcmk_action_t *action)
 457 {
 458     GList *stop_ops = pe__resource_actions(rsc, node, PCMK_ACTION_STOP, TRUE);
 459 
 460     for (GList *iter = stop_ops; iter != NULL; iter = iter->next) {
 461         pcmk_action_t *stop = (pcmk_action_t *) iter->data;
 462 
 463         if (!pcmk_is_set(stop->flags, pcmk_action_optional)
 464             && !pcmk_is_set(action->flags, pcmk_action_optional)
 465             && !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 466             pcmk__rsc_trace(rsc, "%s optional on %s: unmanaged",
 467                             action->uuid, pcmk__node_name(node));
 468             pcmk__set_action_flags(action, pcmk_action_optional);
 469         }
 470 
 471         if (!pcmk_is_set(stop->flags, pcmk_action_runnable)) {
 472             crm_debug("%s unrunnable on %s: stop is unrunnable",
 473                       action->uuid, pcmk__node_name(node));
 474             pcmk__clear_action_flags(action, pcmk_action_runnable);
 475         }
 476 
 477         if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 478             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     g_list_free(stop_ops);
 486 }
 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 recurring_op_for_inactive(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 498                           const struct op_history *op)
 499 {
 500     GList *possible_matches = NULL;
 501 
 502     // We're only interested in recurring actions for the inactive role
 503     if (op->role != pcmk_role_stopped) {
 504         return;
 505     }
 506 
 507     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
 508         crm_notice("Ignoring %s (recurring monitors for " PCMK_ROLE_STOPPED
 509                    " role are not supported for anonymous clones)", op->id);
 510         return; // @TODO add support
 511     }
 512 
 513     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     for (GList *iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) {
 518         pcmk_node_t *stop_node = (pcmk_node_t *) iter->data;
 519 
 520         bool is_optional = true;
 521         pcmk_action_t *stopped_mon = NULL;
 522 
 523         // Cancel action on node where resource will be active
 524         if ((node != NULL)
 525             && pcmk__str_eq(stop_node->details->uname, node->details->uname,
 526                             pcmk__str_casei)) {
 527             cancel_if_running(rsc, node, op->key, op->name, op->interval_ms);
 528             continue;
 529         }
 530 
 531         // Recurring action on this node is optional if it's already active here
 532         possible_matches = find_actions_exact(rsc->actions, op->key, stop_node);
 533         is_optional = (possible_matches != NULL);
 534         g_list_free(possible_matches);
 535 
 536         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         stopped_mon = custom_action(rsc, strdup(op->key), op->name, stop_node,
 543                                     is_optional, rsc->cluster);
 544 
 545         pe__add_action_expected_result(stopped_mon, CRM_EX_NOT_RUNNING);
 546 
 547         if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 548             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         order_after_stops(rsc, stop_node, stopped_mon);
 555 
 556         if (!stop_node->details->online || stop_node->details->unclean) {
 557             pcmk__rsc_debug(rsc, "%s unrunnable on %s: node unavailable)",
 558                             stopped_mon->uuid, pcmk__node_name(stop_node));
 559             pcmk__clear_action_flags(stopped_mon, pcmk_action_runnable);
 560         }
 561 
 562         if (pcmk_is_set(stopped_mon->flags, pcmk_action_runnable)
 563             && !pcmk_is_set(stopped_mon->flags, pcmk_action_optional)) {
 564             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 pcmk__create_recurring_actions(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 580 {
 581     pcmk_action_t *start = NULL;
 582 
 583     if (pcmk_is_set(rsc->flags, pcmk_rsc_blocked)) {
 584         pcmk__rsc_trace(rsc,
 585                         "Skipping recurring actions for blocked resource %s",
 586                         rsc->id);
 587         return;
 588     }
 589 
 590     if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
 591         pcmk__rsc_trace(rsc,
 592                         "Skipping recurring actions for %s "
 593                         "in maintenance mode", rsc->id);
 594         return;
 595     }
 596 
 597     if (rsc->allocated_to == NULL) {
 598         // Recurring actions for active roles not needed
 599 
 600     } else if (rsc->allocated_to->details->maintenance) {
 601         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     } else if ((rsc->next_role != pcmk_role_stopped)
 607         || !pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 608         // Recurring actions for active roles needed
 609         start = start_action(rsc, rsc->allocated_to, TRUE);
 610     }
 611 
 612     pcmk__rsc_trace(rsc, "Creating any recurring actions needed for %s",
 613                     rsc->id);
 614 
 615     for (xmlNode *op = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP, NULL,
 616                                             NULL);
 617          op != NULL; op = pcmk__xe_next_same(op)) {
 618 
 619         struct op_history op_history = { NULL, };
 620 
 621         if (!is_recurring_history(rsc, op, &op_history)) {
 622             continue;
 623         }
 624 
 625         if (start != NULL) {
 626             recurring_op_for_active(rsc, start, rsc->allocated_to, &op_history);
 627         }
 628         recurring_op_for_inactive(rsc, rsc->allocated_to, &op_history);
 629 
 630         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 pcmk__new_cancel_action(pcmk_resource_t *rsc, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 647                         guint interval_ms, const pcmk_node_t *node)
 648 {
 649     pcmk_action_t *cancel_op = NULL;
 650     char *key = NULL;
 651     char *interval_ms_s = NULL;
 652 
 653     pcmk__assert((rsc != NULL) && (task != NULL) && (node != NULL));
 654 
 655     key = pcmk__op_key(rsc->id, task, interval_ms);
 656 
 657     /* This finds an existing action by key, so custom_action() does not change
 658      * cancel_op->task.
 659      */
 660     cancel_op = custom_action(rsc, key, PCMK_ACTION_CANCEL, node, FALSE,
 661                               rsc->cluster);
 662 
 663     pcmk__str_update(&(cancel_op->task), PCMK_ACTION_CANCEL);
 664     pcmk__str_update(&(cancel_op->cancel_task), task);
 665 
 666     interval_ms_s = crm_strdup_printf("%u", interval_ms);
 667     pcmk__insert_meta(cancel_op, PCMK_XA_OPERATION, task);
 668     pcmk__insert_meta(cancel_op, PCMK_META_INTERVAL, interval_ms_s);
 669     free(interval_ms_s);
 670 
 671     return cancel_op;
 672 }
 673 
 674 /*!
 675  * \internal
 676  * \brief Schedule cancellation of a recurring action
 677  *
 678  * \param[in,out] rsc          Resource that action is for
 679  * \param[in]     call_id      Action's call ID from history
 680  * \param[in]     task         Action name
 681  * \param[in]     interval_ms  Action interval
 682  * \param[in]     node         Node that history entry is for
 683  * \param[in]     reason       Short description of why action is cancelled
 684  */
 685 void
 686 pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 687                       const char *task, guint interval_ms,
 688                       const pcmk_node_t *node, const char *reason)
 689 {
 690     pcmk_action_t *cancel = NULL;
 691 
 692     CRM_CHECK((rsc != NULL) && (task != NULL)
 693               && (node != NULL) && (reason != NULL),
 694               return);
 695 
 696     crm_info("Recurring %s-interval %s for %s will be stopped on %s: %s",
 697              pcmk__readable_interval(interval_ms), task, rsc->id,
 698              pcmk__node_name(node), reason);
 699     cancel = pcmk__new_cancel_action(rsc, task, interval_ms, node);
 700     pcmk__insert_meta(cancel, PCMK__XA_CALL_ID, call_id);
 701 
 702     // Cancellations happen after stops
 703     pcmk__new_ordering(rsc, stop_key(rsc), NULL, rsc, NULL, cancel,
 704                        pcmk__ar_ordered, rsc->cluster);
 705 }
 706 
 707 /*!
 708  * \internal
 709  * \brief Create a recurring action marked as needing rescheduling if active
 710  *
 711  * \param[in,out] rsc          Resource that action is for
 712  * \param[in]     task         Name of action being rescheduled
 713  * \param[in]     interval_ms  Action interval (in milliseconds)
 714  * \param[in,out] node         Node where action should be rescheduled
 715  */
 716 void
 717 pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 718                            guint interval_ms, pcmk_node_t *node)
 719 {
 720     pcmk_action_t *op = NULL;
 721 
 722     trigger_unfencing(rsc, node, "Device parameters changed (reschedule)",
 723                       NULL, rsc->cluster);
 724     op = custom_action(rsc, pcmk__op_key(rsc->id, task, interval_ms),
 725                        task, node, TRUE, rsc->cluster);
 726     pcmk__set_action_flags(op, pcmk_action_reschedule);
 727 }
 728 
 729 /*!
 730  * \internal
 731  * \brief Check whether an action is recurring
 732  *
 733  * \param[in] action  Action to check
 734  *
 735  * \return true if \p action has a nonzero interval, otherwise false
 736  */
 737 bool
 738 pcmk__action_is_recurring(const pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 739 {
 740     guint interval_ms = 0;
 741 
 742     if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
 743                               &interval_ms) != pcmk_rc_ok) {
 744         return false;
 745     }
 746     return (interval_ms > 0);
 747 }

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