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-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 /*!
  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     add_hash_param(action->meta, XML_LRM_ATTR_MIGRATE_SOURCE,
  32                    source->details->uname);
  33 
  34     add_hash_param(action->meta, XML_LRM_ATTR_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     pe_rsc_trace(rsc, "Creating actions to %smigrate %s from %s to %s",
  54                  ((rsc->partial_migration_target == NULL)? "" : "partially "),
  55                  rsc->id, pe__node_name(current),
  56                  pe__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     pe__set_action_flags(start, pcmk_action_migratable);
  72     pe__set_action_flags(stop, pcmk_action_migratable);
  73 
  74     // This is easier than trying to delete it from the graph
  75     pe__set_action_flags(start, pcmk_action_pseudo);
  76 
  77     if (rsc->partial_migration_target == NULL) {
  78         pe__set_action_flags(migrate_from, pcmk_action_migratable);
  79         pe__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         pe__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 record-pending defaults to true, skipping it
 136              * matters even less.)
 137              */
 138             add_hash_param(migrate_to->meta, XML_OP_ATTR_PENDING, "true");
 139         }
 140     }
 141 
 142     add_migration_meta(migrate_from, current, rsc->allocated_to);
 143 }
 144 
 145 /*!
 146  * \internal
 147  * \brief Abort a dangling migration by scheduling a stop (and possibly cleanup)
 148  *
 149  * \param[in]     data       Source node of dangling migration
 150  * \param[in,out] user_data  Resource involved in dangling migration
 151  */
 152 void
 153 pcmk__abort_dangling_migration(void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 154 {
 155     const pcmk_node_t *dangling_source = (const pcmk_node_t *) data;
 156     pcmk_resource_t *rsc = (pcmk_resource_t *) user_data;
 157 
 158     pcmk_action_t *stop = NULL;
 159     bool cleanup = pcmk_is_set(rsc->cluster->flags,
 160                                pcmk_sched_remove_after_stop);
 161 
 162     pe_rsc_trace(rsc,
 163                  "Scheduling stop%s for %s on %s due to dangling migration",
 164                  (cleanup? " and cleanup" : ""), rsc->id,
 165                  pe__node_name(dangling_source));
 166     stop = stop_action(rsc, dangling_source, FALSE);
 167     pe__set_action_flags(stop, pcmk_action_migration_abort);
 168     if (cleanup) {
 169         pcmk__schedule_cleanup(rsc, dangling_source, false);
 170     }
 171 }
 172 
 173 /*!
 174  * \internal
 175  * \brief Check whether a resource can migrate
 176  *
 177  * \param[in] rsc   Resource to check
 178  * \param[in] node  Resource's current node
 179  *
 180  * \return true if \p rsc can migrate, otherwise false
 181  */
 182 bool
 183 pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185     CRM_CHECK(rsc != NULL, return false);
 186 
 187     if (!pcmk_is_set(rsc->flags, pcmk_rsc_migratable)) {
 188         pe_rsc_trace(rsc, "%s cannot migrate because "
 189                           "the configuration does not allow it",
 190                      rsc->id);
 191         return false;
 192     }
 193 
 194     if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 195         pe_rsc_trace(rsc, "%s cannot migrate because it is not managed",
 196                      rsc->id);
 197         return false;
 198     }
 199 
 200     if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
 201         pe_rsc_trace(rsc, "%s cannot migrate because it is failed",
 202                      rsc->id);
 203         return false;
 204     }
 205 
 206     if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) {
 207         pe_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         pe_rsc_trace(rsc, "%s cannot migrate because "
 214                           "its current node (%s) is unclean",
 215                      rsc->id, pe__node_name(current));
 216         return false;
 217     }
 218 
 219     if ((rsc->allocated_to == NULL) || rsc->allocated_to->details->unclean) {
 220         pe_rsc_trace(rsc, "%s cannot migrate because "
 221                           "its next node (%s) is unclean",
 222                      rsc->id, pe__node_name(rsc->allocated_to));
 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 = strdup(action->task);
 245         CRM_ASSERT(res != NULL);
 246     } else if (key != NULL) {
 247         parse_op_key(key, NULL, &res, NULL);
 248     }
 249     return res;
 250 }
 251 
 252 /*!
 253  * \internal
 254  * \brief Order migration actions equivalent to a given ordering
 255  *
 256  * Orderings involving start, stop, demote, and promote actions must be honored
 257  * during a migration as well, so duplicate any such ordering for the
 258  * corresponding migration actions.
 259  *
 260  * \param[in,out] order     Ordering constraint to check
 261  */
 262 void
 263 pcmk__order_migration_equivalents(pe__ordering_t *order)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265     char *first_task = NULL;
 266     char *then_task = NULL;
 267     bool then_migratable;
 268     bool first_migratable;
 269 
 270     // Only orderings between unrelated resources are relevant
 271     if ((order->lh_rsc == NULL) || (order->rh_rsc == NULL)
 272         || (order->lh_rsc == order->rh_rsc)
 273         || is_parent(order->lh_rsc, order->rh_rsc)
 274         || is_parent(order->rh_rsc, order->lh_rsc)) {
 275         return;
 276     }
 277 
 278     // Only orderings involving at least one migratable resource are relevant
 279     first_migratable = pcmk_is_set(order->lh_rsc->flags, pcmk_rsc_migratable);
 280     then_migratable = pcmk_is_set(order->rh_rsc->flags, pcmk_rsc_migratable);
 281     if (!first_migratable && !then_migratable) {
 282         return;
 283     }
 284 
 285     // Check which actions are involved
 286     first_task = task_from_action_or_key(order->lh_action,
 287                                          order->lh_action_task);
 288     then_task = task_from_action_or_key(order->rh_action,
 289                                         order->rh_action_task);
 290 
 291     if (pcmk__str_eq(first_task, PCMK_ACTION_START, pcmk__str_none)
 292         && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) {
 293 
 294         uint32_t flags = pcmk__ar_ordered;
 295 
 296         if (first_migratable && then_migratable) {
 297             /* A start then B start
 298              * -> A migrate_from then B migrate_to */
 299             pcmk__new_ordering(order->lh_rsc,
 300                                pcmk__op_key(order->lh_rsc->id,
 301                                             PCMK_ACTION_MIGRATE_FROM, 0),
 302                                NULL, order->rh_rsc,
 303                                pcmk__op_key(order->rh_rsc->id,
 304                                             PCMK_ACTION_MIGRATE_TO, 0),
 305                                NULL, flags, order->lh_rsc->cluster);
 306         }
 307 
 308         if (then_migratable) {
 309             if (first_migratable) {
 310                 pe__set_order_flags(flags, pcmk__ar_if_first_unmigratable);
 311             }
 312 
 313             /* A start then B start
 314              * -> A start then B migrate_to (if start is not part of a
 315              *    migration)
 316              */
 317             pcmk__new_ordering(order->lh_rsc,
 318                                pcmk__op_key(order->lh_rsc->id,
 319                                             PCMK_ACTION_START, 0),
 320                                NULL, order->rh_rsc,
 321                                pcmk__op_key(order->rh_rsc->id,
 322                                             PCMK_ACTION_MIGRATE_TO, 0),
 323                                NULL, flags, order->lh_rsc->cluster);
 324         }
 325 
 326     } else if (then_migratable
 327                && pcmk__str_eq(first_task, PCMK_ACTION_STOP, pcmk__str_none)
 328                && pcmk__str_eq(then_task, PCMK_ACTION_STOP, pcmk__str_none)) {
 329 
 330         uint32_t flags = pcmk__ar_ordered;
 331 
 332         if (first_migratable) {
 333             pe__set_order_flags(flags, pcmk__ar_if_first_unmigratable);
 334         }
 335 
 336         /* For an ordering "stop A then stop B", if A is moving via restart, and
 337          * B is migrating, enforce that B's migrate_to occurs after A's stop.
 338          */
 339         pcmk__new_ordering(order->lh_rsc,
 340                            pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_STOP, 0),
 341                            NULL,
 342                            order->rh_rsc,
 343                            pcmk__op_key(order->rh_rsc->id,
 344                                         PCMK_ACTION_MIGRATE_TO, 0),
 345                            NULL, flags, order->lh_rsc->cluster);
 346 
 347         // Also order B's migrate_from after A's stop during partial migrations
 348         if (order->rh_rsc->partial_migration_target) {
 349             pcmk__new_ordering(order->lh_rsc,
 350                                pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_STOP,
 351                                             0),
 352                                NULL, order->rh_rsc,
 353                                pcmk__op_key(order->rh_rsc->id,
 354                                             PCMK_ACTION_MIGRATE_FROM, 0),
 355                                NULL, flags, order->lh_rsc->cluster);
 356         }
 357 
 358     } else if (pcmk__str_eq(first_task, PCMK_ACTION_PROMOTE, pcmk__str_none)
 359                && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) {
 360 
 361         uint32_t flags = pcmk__ar_ordered;
 362 
 363         if (then_migratable) {
 364             /* A promote then B start
 365              * -> A promote then B migrate_to */
 366             pcmk__new_ordering(order->lh_rsc,
 367                                pcmk__op_key(order->lh_rsc->id,
 368                                             PCMK_ACTION_PROMOTE, 0),
 369                                NULL, order->rh_rsc,
 370                                pcmk__op_key(order->rh_rsc->id,
 371                                             PCMK_ACTION_MIGRATE_TO, 0),
 372                                NULL, flags, order->lh_rsc->cluster);
 373         }
 374 
 375     } else if (pcmk__str_eq(first_task, PCMK_ACTION_DEMOTE, pcmk__str_none)
 376                && pcmk__str_eq(then_task, PCMK_ACTION_STOP, pcmk__str_none)) {
 377 
 378         uint32_t flags = pcmk__ar_ordered;
 379 
 380         if (then_migratable) {
 381             /* A demote then B stop
 382              * -> A demote then B migrate_to */
 383             pcmk__new_ordering(order->lh_rsc,
 384                                pcmk__op_key(order->lh_rsc->id,
 385                                             PCMK_ACTION_DEMOTE, 0),
 386                                NULL, order->rh_rsc,
 387                                pcmk__op_key(order->rh_rsc->id,
 388                                             PCMK_ACTION_MIGRATE_TO, 0),
 389                                NULL, flags, order->lh_rsc->cluster);
 390 
 391             // Order B migrate_from after A demote during partial migrations
 392             if (order->rh_rsc->partial_migration_target) {
 393                 pcmk__new_ordering(order->lh_rsc,
 394                                    pcmk__op_key(order->lh_rsc->id,
 395                                                 PCMK_ACTION_DEMOTE, 0),
 396                                    NULL, order->rh_rsc,
 397                                    pcmk__op_key(order->rh_rsc->id,
 398                                                 PCMK_ACTION_MIGRATE_FROM, 0),
 399                                    NULL, flags, order->lh_rsc->cluster);
 400             }
 401         }
 402     }
 403 
 404     free(first_task);
 405     free(then_task);
 406 }

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