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

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