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

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