root/lib/pacemaker/pcmk_sched_migration.c

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

DEFINITIONS

This source file includes following definitions.
  1. add_migration_meta
  2. pcmk__create_migration_actions
  3. pcmk__abort_dangling_migration
  4. pcmk__rsc_can_migrate
  5. task_from_action_or_key
  6. pcmk__order_migration_equivalents

   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 <pacemaker-internal.h>
  16 
  17 #include "libpacemaker_private.h"
  18 
  19 /*!
  20  * \internal
  21  * \brief Add migration source and target meta-attributes to an action
  22  *
  23  * \param[in,out] action  Action to add meta-attributes to
  24  * \param[in]     source  Node to add as migration source
  25  * \param[in]     target  Node to add as migration target
  26  */
  27 static void
  28 add_migration_meta(pcmk_action_t *action, const pcmk_node_t *source,
     /* [previous][next][first][last][top][bottom][index][help] */
  29                    const pcmk_node_t *target)
  30 {
  31     pcmk__insert_meta(action, PCMK__META_MIGRATE_SOURCE,
  32                       source->details->uname);
  33 
  34     pcmk__insert_meta(action, PCMK__META_MIGRATE_TARGET,
  35                       target->details->uname);
  36 }
  37 
  38 /*!
  39  * \internal
  40  * \brief Create internal migration actions for a migrateable resource
  41  *
  42  * \param[in,out] rsc      Resource to create migration actions for
  43  * \param[in]     current  Node that resource is originally active on
  44  */
  45 void
  46 pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     pcmk_action_t *migrate_to = NULL;
  49     pcmk_action_t *migrate_from = NULL;
  50     pcmk_action_t *start = NULL;
  51     pcmk_action_t *stop = NULL;
  52 
  53     pcmk__rsc_trace(rsc, "Creating actions to %smigrate %s from %s to %s",
  54                     ((rsc->partial_migration_target == NULL)? "" : "partially "),
  55                     rsc->id, pcmk__node_name(current),
  56                     pcmk__node_name(rsc->allocated_to));
  57     start = start_action(rsc, rsc->allocated_to, TRUE);
  58     stop = stop_action(rsc, current, TRUE);
  59 
  60     if (rsc->partial_migration_target == NULL) {
  61         migrate_to = custom_action(rsc, pcmk__op_key(rsc->id,
  62                                                      PCMK_ACTION_MIGRATE_TO, 0),
  63                                    PCMK_ACTION_MIGRATE_TO, current, TRUE,
  64                                    rsc->cluster);
  65     }
  66     migrate_from = custom_action(rsc, pcmk__op_key(rsc->id,
  67                                                    PCMK_ACTION_MIGRATE_FROM, 0),
  68                                  PCMK_ACTION_MIGRATE_FROM, rsc->allocated_to,
  69                                  TRUE, rsc->cluster);
  70 
  71     pcmk__set_action_flags(start, pcmk_action_migratable);
  72     pcmk__set_action_flags(stop, pcmk_action_migratable);
  73 
  74     // This is easier than trying to delete it from the graph
  75     pcmk__set_action_flags(start, pcmk_action_pseudo);
  76 
  77     if (rsc->partial_migration_target == NULL) {
  78         pcmk__set_action_flags(migrate_from, pcmk_action_migratable);
  79         pcmk__set_action_flags(migrate_to, pcmk_action_migratable);
  80         migrate_to->needs = start->needs;
  81 
  82         // Probe -> migrate_to -> migrate_from
  83         pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0),
  84                            NULL,
  85                            rsc,
  86                            pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0),
  87                            NULL, pcmk__ar_ordered, rsc->cluster);
  88         pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0),
  89                            NULL,
  90                            rsc,
  91                            pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0),
  92                            NULL,
  93                            pcmk__ar_ordered|pcmk__ar_unmigratable_then_blocks,
  94                            rsc->cluster);
  95     } else {
  96         pcmk__set_action_flags(migrate_from, pcmk_action_migratable);
  97         migrate_from->needs = start->needs;
  98 
  99         // Probe -> migrate_from (migrate_to already completed)
 100         pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0),
 101                            NULL,
 102                            rsc,
 103                            pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0),
 104                            NULL, pcmk__ar_ordered, rsc->cluster);
 105     }
 106 
 107     // migrate_from before stop or start
 108     pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0),
 109                        NULL,
 110                        rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0),
 111                        NULL,
 112                        pcmk__ar_ordered|pcmk__ar_unmigratable_then_blocks,
 113                        rsc->cluster);
 114     pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0),
 115                        NULL,
 116                        rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0),
 117                        NULL,
 118                        pcmk__ar_ordered
 119                        |pcmk__ar_unmigratable_then_blocks
 120                        |pcmk__ar_first_else_then,
 121                        rsc->cluster);
 122 
 123     if (migrate_to != NULL) {
 124         add_migration_meta(migrate_to, current, rsc->allocated_to);
 125 
 126         if (!rsc->is_remote_node) {
 127             /* migrate_to takes place on the source node, but can affect the
 128              * target node depending on how the agent is written. Because of
 129              * this, pending migrate_to actions must be recorded in the CIB,
 130              * in case the source node loses membership while the migrate_to
 131              * action is still in flight.
 132              *
 133              * However we know Pacemaker Remote connection resources don't
 134              * require this, so we skip this for them. (Although it wouldn't
 135              * hurt, and now that PCMK_META_RECORD_PENDING defaults to true,
 136              * skipping it matters even less.)
 137              */
 138             pcmk__insert_meta(migrate_to,
 139                               PCMK_META_RECORD_PENDING, PCMK_VALUE_TRUE);
 140         }
 141     }
 142 
 143     add_migration_meta(migrate_from, current, rsc->allocated_to);
 144 }
 145 
 146 /*!
 147  * \internal
 148  * \brief Abort a dangling migration by scheduling a stop (and possibly cleanup)
 149  *
 150  * \param[in]     data       Source node of dangling migration
 151  * \param[in,out] user_data  Resource involved in dangling migration
 152  */
 153 void
 154 pcmk__abort_dangling_migration(void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156     const pcmk_node_t *dangling_source = (const pcmk_node_t *) data;
 157     pcmk_resource_t *rsc = (pcmk_resource_t *) user_data;
 158 
 159     pcmk_action_t *stop = NULL;
 160     bool cleanup = pcmk_is_set(rsc->cluster->flags,
 161                                pcmk_sched_remove_after_stop);
 162 
 163     pcmk__rsc_trace(rsc,
 164                     "Scheduling stop%s for %s on %s due to dangling migration",
 165                     (cleanup? " and cleanup" : ""), rsc->id,
 166                     pcmk__node_name(dangling_source));
 167     stop = stop_action(rsc, dangling_source, FALSE);
 168     pcmk__set_action_flags(stop, pcmk_action_migration_abort);
 169     if (cleanup) {
 170         pcmk__schedule_cleanup(rsc, dangling_source, false);
 171     }
 172 }
 173 
 174 /*!
 175  * \internal
 176  * \brief Check whether a resource can migrate
 177  *
 178  * \param[in] rsc   Resource to check
 179  * \param[in] node  Resource's current node
 180  *
 181  * \return true if \p rsc can migrate, otherwise false
 182  */
 183 bool
 184 pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     CRM_CHECK(rsc != NULL, return false);
 187 
 188     if (!pcmk_is_set(rsc->flags, pcmk_rsc_migratable)) {
 189         pcmk__rsc_trace(rsc,
 190                         "%s cannot migrate because "
 191                         "the configuration does not allow it", rsc->id);
 192         return false;
 193     }
 194 
 195     if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 196         pcmk__rsc_trace(rsc, "%s cannot migrate because it is not managed",
 197                         rsc->id);
 198         return false;
 199     }
 200 
 201     if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
 202         pcmk__rsc_trace(rsc, "%s cannot migrate because it is failed", rsc->id);
 203         return false;
 204     }
 205 
 206     if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) {
 207         pcmk__rsc_trace(rsc, "%s cannot migrate because it has a start pending",
 208                         rsc->id);
 209         return false;
 210     }
 211 
 212     if ((current == NULL) || current->details->unclean) {
 213         pcmk__rsc_trace(rsc,
 214                         "%s cannot migrate because "
 215                         "its current node (%s) is unclean",
 216                         rsc->id, pcmk__node_name(current));
 217         return false;
 218     }
 219 
 220     if ((rsc->allocated_to == NULL) || rsc->allocated_to->details->unclean) {
 221         pcmk__rsc_trace(rsc,
 222                         "%s cannot migrate because "
 223                         "its next node (%s) is unclean",
 224                         rsc->id, pcmk__node_name(rsc->allocated_to));
 225         return false;
 226     }
 227 
 228     return true;
 229 }
 230 
 231 /*!
 232  * \internal
 233  * \brief Get an action name from an action or operation key
 234  *
 235  * \param[in] action  If not NULL, get action name from here
 236  * \param[in] key     If not NULL, get action name from here
 237  *
 238  * \return Newly allocated copy of action name (or NULL if none available)
 239  */
 240 static char *
 241 task_from_action_or_key(const pcmk_action_t *action, const char *key)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243     char *res = NULL;
 244 
 245     if (action != NULL) {
 246         res = pcmk__str_copy(action->task);
 247     } else if (key != NULL) {
 248         parse_op_key(key, NULL, &res, NULL);
 249     }
 250     return res;
 251 }
 252 
 253 /*!
 254  * \internal
 255  * \brief Order migration actions equivalent to a given ordering
 256  *
 257  * Orderings involving start, stop, demote, and promote actions must be honored
 258  * during a migration as well, so duplicate any such ordering for the
 259  * corresponding migration actions.
 260  *
 261  * \param[in,out] order     Ordering constraint to check
 262  */
 263 void
 264 pcmk__order_migration_equivalents(pcmk__action_relation_t *order)
     /* [previous][next][first][last][top][bottom][index][help] */
 265 {
 266     char *first_task = NULL;
 267     char *then_task = NULL;
 268     bool then_migratable;
 269     bool first_migratable;
 270 
 271     // Only orderings between unrelated resources are relevant
 272     if ((order->rsc1 == NULL) || (order->rsc2 == NULL)
 273         || (order->rsc1 == order->rsc2)
 274         || is_parent(order->rsc1, order->rsc2)
 275         || is_parent(order->rsc2, order->rsc1)) {
 276         return;
 277     }
 278 
 279     // Only orderings involving at least one migratable resource are relevant
 280     first_migratable = pcmk_is_set(order->rsc1->flags, pcmk_rsc_migratable);
 281     then_migratable = pcmk_is_set(order->rsc2->flags, pcmk_rsc_migratable);
 282     if (!first_migratable && !then_migratable) {
 283         return;
 284     }
 285 
 286     // Check which actions are involved
 287     first_task = task_from_action_or_key(order->action1, order->task1);
 288     then_task = task_from_action_or_key(order->action2, order->task2);
 289 
 290     if (pcmk__str_eq(first_task, PCMK_ACTION_START, pcmk__str_none)
 291         && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) {
 292 
 293         uint32_t flags = pcmk__ar_ordered;
 294 
 295         if (first_migratable && then_migratable) {
 296             /* A start then B start
 297              * -> A migrate_from then B migrate_to */
 298             pcmk__new_ordering(order->rsc1,
 299                                pcmk__op_key(order->rsc1->id,
 300                                             PCMK_ACTION_MIGRATE_FROM, 0),
 301                                NULL, order->rsc2,
 302                                pcmk__op_key(order->rsc2->id,
 303                                             PCMK_ACTION_MIGRATE_TO, 0),
 304                                NULL, flags, order->rsc1->cluster);
 305         }
 306 
 307         if (then_migratable) {
 308             if (first_migratable) {
 309                 pcmk__set_relation_flags(flags, pcmk__ar_if_first_unmigratable);
 310             }
 311 
 312             /* A start then B start
 313              * -> A start then B migrate_to (if start is not part of a
 314              *    migration)
 315              */
 316             pcmk__new_ordering(order->rsc1,
 317                                pcmk__op_key(order->rsc1->id,
 318                                             PCMK_ACTION_START, 0),
 319                                NULL, order->rsc2,
 320                                pcmk__op_key(order->rsc2->id,
 321                                             PCMK_ACTION_MIGRATE_TO, 0),
 322                                NULL, flags, order->rsc1->cluster);
 323         }
 324 
 325     } else if (then_migratable
 326                && pcmk__str_eq(first_task, PCMK_ACTION_STOP, pcmk__str_none)
 327                && pcmk__str_eq(then_task, PCMK_ACTION_STOP, pcmk__str_none)) {
 328 
 329         uint32_t flags = pcmk__ar_ordered;
 330 
 331         if (first_migratable) {
 332             pcmk__set_relation_flags(flags, pcmk__ar_if_first_unmigratable);
 333         }
 334 
 335         /* For an ordering "stop A then stop B", if A is moving via restart, and
 336          * B is migrating, enforce that B's migrate_to occurs after A's stop.
 337          */
 338         pcmk__new_ordering(order->rsc1,
 339                            pcmk__op_key(order->rsc1->id, PCMK_ACTION_STOP, 0),
 340                            NULL,
 341                            order->rsc2,
 342                            pcmk__op_key(order->rsc2->id,
 343                                         PCMK_ACTION_MIGRATE_TO, 0),
 344                            NULL, flags, order->rsc1->cluster);
 345 
 346         // Also order B's migrate_from after A's stop during partial migrations
 347         if (order->rsc2->partial_migration_target != NULL) {
 348             pcmk__new_ordering(order->rsc1,
 349                                pcmk__op_key(order->rsc1->id, PCMK_ACTION_STOP,
 350                                             0),
 351                                NULL, order->rsc2,
 352                                pcmk__op_key(order->rsc2->id,
 353                                             PCMK_ACTION_MIGRATE_FROM, 0),
 354                                NULL, flags, order->rsc1->cluster);
 355         }
 356 
 357     } else if (pcmk__str_eq(first_task, PCMK_ACTION_PROMOTE, pcmk__str_none)
 358                && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) {
 359 
 360         uint32_t flags = pcmk__ar_ordered;
 361 
 362         if (then_migratable) {
 363             /* A promote then B start
 364              * -> A promote then B migrate_to */
 365             pcmk__new_ordering(order->rsc1,
 366                                pcmk__op_key(order->rsc1->id,
 367                                             PCMK_ACTION_PROMOTE, 0),
 368                                NULL, order->rsc2,
 369                                pcmk__op_key(order->rsc2->id,
 370                                             PCMK_ACTION_MIGRATE_TO, 0),
 371                                NULL, flags, order->rsc1->cluster);
 372         }
 373 
 374     } else if (pcmk__str_eq(first_task, PCMK_ACTION_DEMOTE, pcmk__str_none)
 375                && pcmk__str_eq(then_task, PCMK_ACTION_STOP, pcmk__str_none)) {
 376 
 377         uint32_t flags = pcmk__ar_ordered;
 378 
 379         if (then_migratable) {
 380             /* A demote then B stop
 381              * -> A demote then B migrate_to */
 382             pcmk__new_ordering(order->rsc1,
 383                                pcmk__op_key(order->rsc1->id,
 384                                             PCMK_ACTION_DEMOTE, 0),
 385                                NULL, order->rsc2,
 386                                pcmk__op_key(order->rsc2->id,
 387                                             PCMK_ACTION_MIGRATE_TO, 0),
 388                                NULL, flags, order->rsc1->cluster);
 389 
 390             // Order B migrate_from after A demote during partial migrations
 391             if (order->rsc2->partial_migration_target != NULL) {
 392                 pcmk__new_ordering(order->rsc1,
 393                                    pcmk__op_key(order->rsc1->id,
 394                                                 PCMK_ACTION_DEMOTE, 0),
 395                                    NULL, order->rsc2,
 396                                    pcmk__op_key(order->rsc2->id,
 397                                                 PCMK_ACTION_MIGRATE_FROM, 0),
 398                                    NULL, flags, order->rsc1->cluster);
 399             }
 400         }
 401     }
 402 
 403     free(first_task);
 404     free(then_task);
 405 }

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