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

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