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-2022 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] 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 (GList *iter = possible_matches; iter != NULL; iter = iter->next) {
 214         pe_action_t *op = (pe_action_t *) iter->data;
 215 
 216         if (pcmk_is_set(op->flags, pe_action_reschedule)) {
 217             pe_rsc_trace(rsc,
 218                          "%s will be mandatory because "
 219                          "it needs to be rescheduled", key);
 220             g_list_free(possible_matches);
 221             return false;
 222         }
 223     }
 224 
 225     g_list_free(possible_matches);
 226     return true;
 227 }
 228 
 229 /*!
 230  * \internal
 231  * \brief Create recurring action from resource history entry for an active role
 232  *
 233  * \param[in,out] rsc    Resource that resource history is for
 234  * \param[in]     start  Start action for \p rsc on \p node
 235  * \param[in]     node   Node that resource will be active on (if any)
 236  * \param[in]     op     Resource history entry
 237  */
 238 static void
 239 recurring_op_for_active(pe_resource_t *rsc, pe_action_t *start,
     /* [previous][next][first][last][top][bottom][index][help] */
 240                         const pe_node_t *node, const struct op_history *op)
 241 {
 242     pe_action_t *mon = NULL;
 243     bool is_optional = true;
 244 
 245     // We're only interested in recurring actions for active roles
 246     if (op->role == RSC_ROLE_STOPPED) {
 247         return;
 248     }
 249 
 250     is_optional = active_recurring_should_be_optional(rsc, node, op->key,
 251                                                       start);
 252 
 253     if (((op->role != RSC_ROLE_UNKNOWN) && (rsc->next_role != op->role))
 254         || ((op->role == RSC_ROLE_UNKNOWN)
 255             && (rsc->next_role == RSC_ROLE_PROMOTED))) {
 256         // Configured monitor role doesn't match role resource will have
 257 
 258         if (is_optional) { // It's running, so cancel it
 259             char *after_key = NULL;
 260             pe_action_t *cancel_op = pcmk__new_cancel_action(rsc, op->name,
 261                                                              op->interval_ms,
 262                                                              node);
 263 
 264             switch (rsc->role) {
 265                 case RSC_ROLE_UNPROMOTED:
 266                 case RSC_ROLE_STARTED:
 267                     if (rsc->next_role == RSC_ROLE_PROMOTED) {
 268                         after_key = promote_key(rsc);
 269 
 270                     } else if (rsc->next_role == RSC_ROLE_STOPPED) {
 271                         after_key = stop_key(rsc);
 272                     }
 273 
 274                     break;
 275                 case RSC_ROLE_PROMOTED:
 276                     after_key = demote_key(rsc);
 277                     break;
 278                 default:
 279                     break;
 280             }
 281 
 282             if (after_key) {
 283                 pcmk__new_ordering(rsc, NULL, cancel_op, rsc, after_key, NULL,
 284                                    pe_order_runnable_left, rsc->cluster);
 285             }
 286         }
 287 
 288         do_crm_log((is_optional? LOG_INFO : LOG_TRACE),
 289                    "%s recurring action %s because %s configured for %s role "
 290                    "(not %s)",
 291                    (is_optional? "Cancelling" : "Ignoring"), op->key, op->id,
 292                    role2text((op->role == RSC_ROLE_UNKNOWN)? RSC_ROLE_UNPROMOTED : op->role),
 293                    role2text(rsc->next_role));
 294         return;
 295     }
 296 
 297     pe_rsc_trace(rsc,
 298                  "Creating %s recurring action %s for %s (%s %s on %s)",
 299                  (is_optional? "optional" : "mandatory"), op->key,
 300                  op->id, rsc->id, role2text(rsc->next_role),
 301                  pe__node_name(node));
 302 
 303     mon = custom_action(rsc, strdup(op->key), op->name, node, is_optional, TRUE,
 304                         rsc->cluster);
 305 
 306     if (!pcmk_is_set(start->flags, pe_action_runnable)) {
 307         pe_rsc_trace(rsc, "%s is unrunnable because start is", mon->uuid);
 308         pe__clear_action_flags(mon, pe_action_runnable);
 309 
 310     } else if ((node == NULL) || !node->details->online
 311                || node->details->unclean) {
 312         pe_rsc_trace(rsc, "%s is unrunnable because no node is available",
 313                      mon->uuid);
 314         pe__clear_action_flags(mon, pe_action_runnable);
 315 
 316     } else if (!pcmk_is_set(mon->flags, pe_action_optional)) {
 317         pe_rsc_info(rsc, "Start %s-interval %s for %s on %s",
 318                     pcmk__readable_interval(op->interval_ms), mon->task,
 319                     rsc->id, pe__node_name(node));
 320     }
 321 
 322     if (rsc->next_role == RSC_ROLE_PROMOTED) {
 323         pe__add_action_expected_result(mon, CRM_EX_PROMOTED);
 324     }
 325 
 326     // Order monitor relative to other actions
 327     if ((node == NULL) || pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 328         pcmk__new_ordering(rsc, start_key(rsc), NULL,
 329                            NULL, strdup(mon->uuid), mon,
 330                            pe_order_implies_then|pe_order_runnable_left,
 331                            rsc->cluster);
 332 
 333         pcmk__new_ordering(rsc, reload_key(rsc), NULL,
 334                            NULL, strdup(mon->uuid), mon,
 335                            pe_order_implies_then|pe_order_runnable_left,
 336                            rsc->cluster);
 337 
 338         if (rsc->next_role == RSC_ROLE_PROMOTED) {
 339             pcmk__new_ordering(rsc, promote_key(rsc), NULL,
 340                                rsc, NULL, mon,
 341                                pe_order_optional|pe_order_runnable_left,
 342                                rsc->cluster);
 343 
 344         } else if (rsc->role == RSC_ROLE_PROMOTED) {
 345             pcmk__new_ordering(rsc, demote_key(rsc), NULL,
 346                                rsc, NULL, mon,
 347                                pe_order_optional|pe_order_runnable_left,
 348                                rsc->cluster);
 349         }
 350     }
 351 }
 352 
 353 /*!
 354  * \internal
 355  * \brief Cancel a recurring action if running on a node
 356  *
 357  * \param[in,out] rsc          Resource that action is for
 358  * \param[in]     node         Node to cancel action on
 359  * \param[in]     key          Operation key for action
 360  * \param[in]     name         Action name
 361  * \param[in]     interval_ms  Action interval (in milliseconds)
 362  */
 363 static void
 364 cancel_if_running(pe_resource_t *rsc, const pe_node_t *node, const char *key,
     /* [previous][next][first][last][top][bottom][index][help] */
 365                   const char *name, guint interval_ms)
 366 {
 367     GList *possible_matches = find_actions_exact(rsc->actions, key, node);
 368     pe_action_t *cancel_op = NULL;
 369 
 370     if (possible_matches == NULL) {
 371         return; // Recurring action isn't running on this node
 372     }
 373     g_list_free(possible_matches);
 374 
 375     cancel_op = pcmk__new_cancel_action(rsc, name, interval_ms, node);
 376 
 377     switch (rsc->next_role) {
 378         case RSC_ROLE_STARTED:
 379         case RSC_ROLE_UNPROMOTED:
 380             /* Order starts after cancel. If the current role is
 381              * stopped, this cancels the monitor before the resource
 382              * starts; if the current role is started, then this cancels
 383              * the monitor on a migration target before starting there.
 384              */
 385             pcmk__new_ordering(rsc, NULL, cancel_op,
 386                                rsc, start_key(rsc), NULL,
 387                                pe_order_runnable_left, rsc->cluster);
 388             break;
 389         default:
 390             break;
 391     }
 392     pe_rsc_info(rsc,
 393                 "Cancelling %s-interval %s action for %s on %s because "
 394                 "configured for " RSC_ROLE_STOPPED_S " role (not %s)",
 395                 pcmk__readable_interval(interval_ms), name, rsc->id,
 396                 pe__node_name(node), role2text(rsc->next_role));
 397 }
 398 
 399 /*!
 400  * \internal
 401  * \brief Order an action after all probes of a resource on a node
 402  *
 403  * \param[in,out] rsc     Resource to check for probes
 404  * \param[in]     node    Node to check for probes of \p rsc
 405  * \param[in,out] action  Action to order after probes of \p rsc on \p node
 406  */
 407 static void
 408 order_after_probes(pe_resource_t *rsc, const pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 409                    pe_action_t *action)
 410 {
 411     GList *probes = pe__resource_actions(rsc, node, RSC_STATUS, FALSE);
 412 
 413     for (GList *iter = probes; iter != NULL; iter = iter->next) {
 414         order_actions((pe_action_t *) iter->data, action,
 415                       pe_order_runnable_left);
 416     }
 417     g_list_free(probes);
 418 }
 419 
 420 /*!
 421  * \internal
 422  * \brief Order an action after all stops of a resource on a node
 423  *
 424  * \param[in,out] rsc     Resource to check for stops
 425  * \param[in]     node    Node to check for stops of \p rsc
 426  * \param[in,out] action  Action to order after stops of \p rsc on \p node
 427  */
 428 static void
 429 order_after_stops(pe_resource_t *rsc, const pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 430                   pe_action_t *action)
 431 {
 432     GList *stop_ops = pe__resource_actions(rsc, node, RSC_STOP, TRUE);
 433 
 434     for (GList *iter = stop_ops; iter != NULL; iter = iter->next) {
 435         pe_action_t *stop = (pe_action_t *) iter->data;
 436 
 437         if (!pcmk_is_set(stop->flags, pe_action_optional)
 438             && !pcmk_is_set(action->flags, pe_action_optional)
 439             && !pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 440             pe_rsc_trace(rsc, "%s optional on %s: unmanaged",
 441                          action->uuid, pe__node_name(node));
 442             pe__set_action_flags(action, pe_action_optional);
 443         }
 444 
 445         if (!pcmk_is_set(stop->flags, pe_action_runnable)) {
 446             crm_debug("%s unrunnable on %s: stop is unrunnable",
 447                       action->uuid, pe__node_name(node));
 448             pe__clear_action_flags(action, pe_action_runnable);
 449         }
 450 
 451         if (pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 452             pcmk__new_ordering(rsc, stop_key(rsc), stop,
 453                                NULL, NULL, action,
 454                                pe_order_implies_then|pe_order_runnable_left,
 455                                rsc->cluster);
 456         }
 457     }
 458     g_list_free(stop_ops);
 459 }
 460 
 461 /*!
 462  * \internal
 463  * \brief Create recurring action from resource history entry for inactive role
 464  *
 465  * \param[in,out] rsc    Resource that resource history is for
 466  * \param[in]     node   Node that resource will be active on (if any)
 467  * \param[in]     op     Resource history entry
 468  */
 469 static void
 470 recurring_op_for_inactive(pe_resource_t *rsc, const pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 471                           const struct op_history *op)
 472 {
 473     GList *possible_matches = NULL;
 474 
 475     // We're only interested in recurring actions for the inactive role
 476     if (op->role != RSC_ROLE_STOPPED) {
 477         return;
 478     }
 479 
 480     if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 481         crm_notice("Ignoring %s (recurring monitors for " RSC_ROLE_STOPPED_S
 482                    " role are not supported for anonymous clones)", op->id);
 483         return; // @TODO add support
 484     }
 485 
 486     pe_rsc_trace(rsc, "Creating recurring action %s for %s on nodes "
 487                       "where it should not be running", op->id, rsc->id);
 488 
 489     for (GList *iter = rsc->cluster->nodes; iter != NULL; iter = iter->next) {
 490         pe_node_t *stop_node = (pe_node_t *) iter->data;
 491 
 492         bool is_optional = true;
 493         pe_action_t *stopped_mon = NULL;
 494 
 495         // Cancel action on node where resource will be active
 496         if ((node != NULL)
 497             && pcmk__str_eq(stop_node->details->uname, node->details->uname,
 498                             pcmk__str_casei)) {
 499             cancel_if_running(rsc, node, op->key, op->name, op->interval_ms);
 500             continue;
 501         }
 502 
 503         // Recurring action on this node is optional if it's already active here
 504         possible_matches = find_actions_exact(rsc->actions, op->key, stop_node);
 505         is_optional = (possible_matches != NULL);
 506         g_list_free(possible_matches);
 507 
 508         pe_rsc_trace(rsc,
 509                      "Creating %s recurring action %s for %s (%s "
 510                      RSC_ROLE_STOPPED_S " on %s)",
 511                      (is_optional? "optional" : "mandatory"),
 512                      op->key, op->id, rsc->id, pe__node_name(stop_node));
 513 
 514         stopped_mon = custom_action(rsc, strdup(op->key), op->name, stop_node,
 515                                     is_optional, TRUE, rsc->cluster);
 516 
 517         pe__add_action_expected_result(stopped_mon, CRM_EX_NOT_RUNNING);
 518 
 519         if (pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 520             order_after_probes(rsc, stop_node, stopped_mon);
 521         }
 522 
 523         /* The recurring action is for the inactive role, so it shouldn't be
 524          * performed until the resource is inactive.
 525          */
 526         order_after_stops(rsc, stop_node, stopped_mon);
 527 
 528         if (!stop_node->details->online || stop_node->details->unclean) {
 529             pe_rsc_debug(rsc, "%s unrunnable on %s: node unavailable)",
 530                          stopped_mon->uuid, pe__node_name(stop_node));
 531             pe__clear_action_flags(stopped_mon, pe_action_runnable);
 532         }
 533 
 534         if (pcmk_is_set(stopped_mon->flags, pe_action_runnable)
 535             && !pcmk_is_set(stopped_mon->flags, pe_action_optional)) {
 536             crm_notice("Start recurring %s-interval %s for "
 537                        RSC_ROLE_STOPPED_S " %s on %s",
 538                        pcmk__readable_interval(op->interval_ms),
 539                        stopped_mon->task, rsc->id, pe__node_name(stop_node));
 540         }
 541     }
 542 }
 543 
 544 /*!
 545  * \internal
 546  * \brief Create recurring actions for a resource
 547  *
 548  * \param[in,out] rsc  Resource to create recurring actions for
 549  */
 550 void
 551 pcmk__create_recurring_actions(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 552 {
 553     pe_action_t *start = NULL;
 554 
 555     if (pcmk_is_set(rsc->flags, pe_rsc_block)) {
 556         pe_rsc_trace(rsc, "Skipping recurring actions for blocked resource %s",
 557                      rsc->id);
 558         return;
 559     }
 560 
 561     if (pcmk_is_set(rsc->flags, pe_rsc_maintenance)) {
 562         pe_rsc_trace(rsc, "Skipping recurring actions for %s "
 563                           "in maintenance mode", rsc->id);
 564         return;
 565     }
 566 
 567     if (rsc->allocated_to == NULL) {
 568         // Recurring actions for active roles not needed
 569 
 570     } else if (rsc->allocated_to->details->maintenance) {
 571         pe_rsc_trace(rsc,
 572                      "Skipping recurring actions for %s on %s "
 573                      "in maintenance mode",
 574                      rsc->id, pe__node_name(rsc->allocated_to));
 575 
 576     } else if ((rsc->next_role != RSC_ROLE_STOPPED)
 577         || !pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 578         // Recurring actions for active roles needed
 579         start = start_action(rsc, rsc->allocated_to, TRUE);
 580     }
 581 
 582     pe_rsc_trace(rsc, "Creating any recurring actions needed for %s", rsc->id);
 583 
 584     for (xmlNode *op = first_named_child(rsc->ops_xml, "op");
 585          op != NULL; op = crm_next_same_xml(op)) {
 586 
 587         struct op_history op_history = { NULL, };
 588 
 589         if (!is_recurring_history(rsc, op, &op_history)) {
 590             continue;
 591         }
 592 
 593         if (start != NULL) {
 594             recurring_op_for_active(rsc, start, rsc->allocated_to, &op_history);
 595         }
 596         recurring_op_for_inactive(rsc, rsc->allocated_to, &op_history);
 597 
 598         free(op_history.key);
 599     }
 600 }
 601 
 602 /*!
 603  * \internal
 604  * \brief Create an executor cancel action
 605  *
 606  * \param[in,out] rsc          Resource of action to cancel
 607  * \param[in]     task         Name of action to cancel
 608  * \param[in]     interval_ms  Interval of action to cancel
 609  * \param[in]     node         Node of action to cancel
 610  *
 611  * \return Created op
 612  */
 613 pe_action_t *
 614 pcmk__new_cancel_action(pe_resource_t *rsc, const char *task, guint interval_ms,
     /* [previous][next][first][last][top][bottom][index][help] */
 615                         const pe_node_t *node)
 616 {
 617     pe_action_t *cancel_op = NULL;
 618     char *key = NULL;
 619     char *interval_ms_s = NULL;
 620 
 621     CRM_ASSERT((rsc != NULL) && (task != NULL) && (node != NULL));
 622 
 623     // @TODO dangerous if possible to schedule another action with this key
 624     key = pcmk__op_key(rsc->id, task, interval_ms);
 625 
 626     cancel_op = custom_action(rsc, key, RSC_CANCEL, node, FALSE, TRUE,
 627                               rsc->cluster);
 628 
 629     pcmk__str_update(&cancel_op->task, RSC_CANCEL);
 630     pcmk__str_update(&cancel_op->cancel_task, task);
 631 
 632     interval_ms_s = crm_strdup_printf("%u", interval_ms);
 633     add_hash_param(cancel_op->meta, XML_LRM_ATTR_TASK, task);
 634     add_hash_param(cancel_op->meta, XML_LRM_ATTR_INTERVAL_MS, interval_ms_s);
 635     free(interval_ms_s);
 636 
 637     return cancel_op;
 638 }
 639 
 640 /*!
 641  * \internal
 642  * \brief Schedule cancellation of a recurring action
 643  *
 644  * \param[in,out] rsc          Resource that action is for
 645  * \param[in]     call_id      Action's call ID from history
 646  * \param[in]     task         Action name
 647  * \param[in]     interval_ms  Action interval
 648  * \param[in]     node         Node that history entry is for
 649  * \param[in]     reason       Short description of why action is being cancelled
 650  */
 651 void
 652 pcmk__schedule_cancel(pe_resource_t *rsc, const char *call_id, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 653                       guint interval_ms, const pe_node_t *node,
 654                       const char *reason)
 655 {
 656     pe_action_t *cancel = NULL;
 657 
 658     CRM_CHECK((rsc != NULL) && (task != NULL)
 659               && (node != NULL) && (reason != NULL),
 660               return);
 661 
 662     crm_info("Recurring %s-interval %s for %s will be stopped on %s: %s",
 663              pcmk__readable_interval(interval_ms), task, rsc->id,
 664              pe__node_name(node), reason);
 665     cancel = pcmk__new_cancel_action(rsc, task, interval_ms, node);
 666     add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id);
 667 
 668     // Cancellations happen after stops
 669     pcmk__new_ordering(rsc, stop_key(rsc), NULL, rsc, NULL, cancel,
 670                        pe_order_optional, rsc->cluster);
 671 }
 672 
 673 /*!
 674  * \internal
 675  * \brief Reschedule a recurring action
 676  *
 677  * \param[in,out] rsc          Resource that action is for
 678  * \param[in]     task         Name of action being rescheduled
 679  * \param[in]     interval_ms  Action interval (in milliseconds)
 680  * \param[in]     node         Node where action should be rescheduled
 681  */
 682 void
 683 pcmk__reschedule_recurring(pe_resource_t *rsc, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 684                            guint interval_ms, pe_node_t *node)
 685 {
 686     pe_action_t *op = NULL;
 687 
 688     trigger_unfencing(rsc, node, "Device parameters changed (reschedule)",
 689                       NULL, rsc->cluster);
 690     op = custom_action(rsc, pcmk__op_key(rsc->id, task, interval_ms),
 691                        task, node, TRUE, TRUE, rsc->cluster);
 692     pe__set_action_flags(op, pe_action_reschedule);
 693 }
 694 
 695 /*!
 696  * \internal
 697  * \brief Check whether an action is recurring
 698  *
 699  * \param[in] action  Action to check
 700  *
 701  * \return true if \p action has a nonzero interval, otherwise false
 702  */
 703 bool
 704 pcmk__action_is_recurring(const pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 705 {
 706     guint interval_ms = 0;
 707 
 708     if (pcmk__guint_from_hash(action->meta,
 709                               XML_LRM_ATTR_INTERVAL_MS, 0,
 710                               &interval_ms) != pcmk_rc_ok) {
 711         return false;
 712     }
 713     return (interval_ms > 0);
 714 }

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