diff --git a/lib/pacemaker/pcmk_sched_migration.c b/lib/pacemaker/pcmk_sched_migration.c index fba4d98b9f..93de19fc30 100644 --- a/lib/pacemaker/pcmk_sched_migration.c +++ b/lib/pacemaker/pcmk_sched_migration.c @@ -1,420 +1,408 @@ /* * Copyright 2004-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include "libpacemaker_private.h" /*! * \internal * \brief Add migration source and target meta-attributes to an action * * \param[in,out] action Action to add meta-attributes to * \param[in] source Node to add as migration source * \param[in] target Node to add as migration target */ static void add_migration_meta(pe_action_t *action, const pe_node_t *source, const pe_node_t *target) { add_hash_param(action->meta, XML_LRM_ATTR_MIGRATE_SOURCE, source->details->uname); add_hash_param(action->meta, XML_LRM_ATTR_MIGRATE_TARGET, target->details->uname); } /*! * \internal * \brief Create internal migration actions for a migrateable resource * * \param[in,out] rsc Resource to create migration actions for * \param[in] current Node that resource is originally active on */ void pcmk__create_migration_actions(pe_resource_t *rsc, const pe_node_t *current) { pe_action_t *migrate_to = NULL; pe_action_t *migrate_from = NULL; pe_action_t *start = NULL; pe_action_t *stop = NULL; pe_rsc_trace(rsc, "Creating actions to %smigrate %s from %s to %s", ((rsc->partial_migration_target == NULL)? "" : "partially "), rsc->id, pe__node_name(current), pe__node_name(rsc->allocated_to)); start = start_action(rsc, rsc->allocated_to, TRUE); stop = stop_action(rsc, current, TRUE); if (rsc->partial_migration_target == NULL) { migrate_to = custom_action(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0), PCMK_ACTION_MIGRATE_TO, current, TRUE, TRUE, rsc->cluster); } migrate_from = custom_action(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), PCMK_ACTION_MIGRATE_FROM, rsc->allocated_to, TRUE, TRUE, rsc->cluster); - if ((migrate_from != NULL) - && ((migrate_to != NULL) || (rsc->partial_migration_target != NULL))) { + pe__set_action_flags(start, pcmk_action_migratable); + pe__set_action_flags(stop, pcmk_action_migratable); - pe__set_action_flags(start, pcmk_action_migratable); - pe__set_action_flags(stop, pcmk_action_migratable); + // This is easier than trying to delete it from the graph + pe__set_action_flags(start, pcmk_action_pseudo); - // This is easier than trying to delete it from the graph - pe__set_action_flags(start, pcmk_action_pseudo); - - if (rsc->partial_migration_target == NULL) { - pe__set_action_flags(migrate_from, pcmk_action_migratable); - - if (migrate_to != NULL) { - pe__set_action_flags(migrate_to, pcmk_action_migratable); - migrate_to->needs = start->needs; - } - - // Probe -> migrate_to -> migrate_from - pcmk__new_ordering(rsc, - pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0), - NULL, rsc, - pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, pe_order_optional, rsc->cluster); - pcmk__new_ordering(rsc, - pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0), - NULL, rsc, - pcmk__op_key(rsc->id, - PCMK_ACTION_MIGRATE_FROM, 0), - NULL, - pe_order_optional - |pe_order_implies_first_migratable, - rsc->cluster); - } else { - pe__set_action_flags(migrate_from, pcmk_action_migratable); - migrate_from->needs = start->needs; - - // Probe -> migrate_from (migrate_to already completed) - pcmk__new_ordering(rsc, - pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0), - NULL, rsc, - pcmk__op_key(rsc->id, - PCMK_ACTION_MIGRATE_FROM, 0), - NULL, pe_order_optional, rsc->cluster); - } + if (rsc->partial_migration_target == NULL) { + pe__set_action_flags(migrate_from, pcmk_action_migratable); + pe__set_action_flags(migrate_to, pcmk_action_migratable); + migrate_to->needs = start->needs; - // migrate_from before stop or start - pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, - 0), + // Probe -> migrate_to -> migrate_from + pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0), NULL, - rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0), + rsc, + pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0), + NULL, pe_order_optional, rsc->cluster); + pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0), NULL, - pe_order_optional|pe_order_implies_first_migratable, - rsc->cluster); - pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, - 0), + rsc, + pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), NULL, - rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0), - NULL, pe_order_optional - |pe_order_implies_first_migratable - |pe_order_pseudo_left, + pe_order_optional + |pe_order_implies_first_migratable, rsc->cluster); + } else { + pe__set_action_flags(migrate_from, pcmk_action_migratable); + migrate_from->needs = start->needs; + + // Probe -> migrate_from (migrate_to already completed) + pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MONITOR, 0), + NULL, + rsc, + pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), + NULL, pe_order_optional, rsc->cluster); } + // migrate_from before stop or start + pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), + NULL, + rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0), + NULL, + pe_order_optional|pe_order_implies_first_migratable, + rsc->cluster); + pcmk__new_ordering(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), + NULL, + rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0), + NULL, + pe_order_optional + |pe_order_implies_first_migratable + |pe_order_pseudo_left, + rsc->cluster); + if (migrate_to != NULL) { add_migration_meta(migrate_to, current, rsc->allocated_to); if (!rsc->is_remote_node) { /* migrate_to takes place on the source node, but can affect the * target node depending on how the agent is written. Because of * this, pending migrate_to actions must be recorded in the CIB, * in case the source node loses membership while the migrate_to * action is still in flight. * * However we know Pacemaker Remote connection resources don't * require this, so we skip this for them. (Although it wouldn't * hurt, and now that record-pending defaults to true, skipping it * matters even less.) */ add_hash_param(migrate_to->meta, XML_OP_ATTR_PENDING, "true"); } } - if (migrate_from != NULL) { - add_migration_meta(migrate_from, current, rsc->allocated_to); - } + add_migration_meta(migrate_from, current, rsc->allocated_to); } /*! * \internal * \brief Abort a dangling migration by scheduling a stop (and possibly cleanup) * * \param[in] data Source node of dangling migration * \param[in,out] user_data Resource involved in dangling migration */ void pcmk__abort_dangling_migration(void *data, void *user_data) { const pe_node_t *dangling_source = (const pe_node_t *) data; pe_resource_t *rsc = (pe_resource_t *) user_data; pe_action_t *stop = NULL; bool cleanup = pcmk_is_set(rsc->cluster->flags, pcmk_sched_remove_after_stop); pe_rsc_trace(rsc, "Scheduling stop%s for %s on %s due to dangling migration", (cleanup? " and cleanup" : ""), rsc->id, pe__node_name(dangling_source)); stop = stop_action(rsc, dangling_source, FALSE); pe__set_action_flags(stop, pcmk_action_migration_abort); if (cleanup) { pcmk__schedule_cleanup(rsc, dangling_source, false); } } /*! * \internal * \brief Check whether a resource can migrate * * \param[in] rsc Resource to check * \param[in] node Resource's current node * * \return true if \p rsc can migrate, otherwise false */ bool pcmk__rsc_can_migrate(const pe_resource_t *rsc, const pe_node_t *current) { CRM_CHECK(rsc != NULL, return false); if (!pcmk_is_set(rsc->flags, pcmk_rsc_migratable)) { pe_rsc_trace(rsc, "%s cannot migrate because " "the configuration does not allow it", rsc->id); return false; } if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) { pe_rsc_trace(rsc, "%s cannot migrate because it is not managed", rsc->id); return false; } if (pcmk_is_set(rsc->flags, pcmk_rsc_failed)) { pe_rsc_trace(rsc, "%s cannot migrate because it is failed", rsc->id); return false; } if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) { pe_rsc_trace(rsc, "%s cannot migrate because it has a start pending", rsc->id); return false; } if ((current == NULL) || current->details->unclean) { pe_rsc_trace(rsc, "%s cannot migrate because " "its current node (%s) is unclean", rsc->id, pe__node_name(current)); return false; } if ((rsc->allocated_to == NULL) || rsc->allocated_to->details->unclean) { pe_rsc_trace(rsc, "%s cannot migrate because " "its next node (%s) is unclean", rsc->id, pe__node_name(rsc->allocated_to)); return false; } return true; } /*! * \internal * \brief Get an action name from an action or operation key * * \param[in] action If not NULL, get action name from here * \param[in] key If not NULL, get action name from here * * \return Newly allocated copy of action name (or NULL if none available) */ static char * task_from_action_or_key(const pe_action_t *action, const char *key) { char *res = NULL; if (action != NULL) { res = strdup(action->task); CRM_ASSERT(res != NULL); } else if (key != NULL) { parse_op_key(key, NULL, &res, NULL); } return res; } /*! * \internal * \brief Order migration actions equivalent to a given ordering * * Orderings involving start, stop, demote, and promote actions must be honored * during a migration as well, so duplicate any such ordering for the * corresponding migration actions. * * \param[in,out] order Ordering constraint to check */ void pcmk__order_migration_equivalents(pe__ordering_t *order) { char *first_task = NULL; char *then_task = NULL; bool then_migratable; bool first_migratable; // Only orderings between unrelated resources are relevant if ((order->lh_rsc == NULL) || (order->rh_rsc == NULL) || (order->lh_rsc == order->rh_rsc) || is_parent(order->lh_rsc, order->rh_rsc) || is_parent(order->rh_rsc, order->lh_rsc)) { return; } // Only orderings involving at least one migratable resource are relevant first_migratable = pcmk_is_set(order->lh_rsc->flags, pcmk_rsc_migratable); then_migratable = pcmk_is_set(order->rh_rsc->flags, pcmk_rsc_migratable); if (!first_migratable && !then_migratable) { return; } // Check which actions are involved first_task = task_from_action_or_key(order->lh_action, order->lh_action_task); then_task = task_from_action_or_key(order->rh_action, order->rh_action_task); if (pcmk__str_eq(first_task, PCMK_ACTION_START, pcmk__str_none) && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) { uint32_t flags = pe_order_optional; if (first_migratable && then_migratable) { /* A start then B start * -> A migrate_from then B migrate_to */ pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_TO, 0), NULL, flags, order->lh_rsc->cluster); } if (then_migratable) { if (first_migratable) { pe__set_order_flags(flags, pe_order_apply_first_non_migratable); } /* A start then B start * -> A start then B migrate_to (if start is not part of a * migration) */ pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_START, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_TO, 0), NULL, flags, order->lh_rsc->cluster); } } else if (then_migratable && pcmk__str_eq(first_task, PCMK_ACTION_STOP, pcmk__str_none) && pcmk__str_eq(then_task, PCMK_ACTION_STOP, pcmk__str_none)) { uint32_t flags = pe_order_optional; if (first_migratable) { pe__set_order_flags(flags, pe_order_apply_first_non_migratable); } /* For an ordering "stop A then stop B", if A is moving via restart, and * B is migrating, enforce that B's migrate_to occurs after A's stop. */ pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_STOP, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_TO, 0), NULL, flags, order->lh_rsc->cluster); // Also order B's migrate_from after A's stop during partial migrations if (order->rh_rsc->partial_migration_target) { pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_STOP, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), NULL, flags, order->lh_rsc->cluster); } } else if (pcmk__str_eq(first_task, PCMK_ACTION_PROMOTE, pcmk__str_none) && pcmk__str_eq(then_task, PCMK_ACTION_START, pcmk__str_none)) { uint32_t flags = pe_order_optional; if (then_migratable) { /* A promote then B start * -> A promote then B migrate_to */ pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_PROMOTE, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_TO, 0), NULL, flags, order->lh_rsc->cluster); } } else if (pcmk__str_eq(first_task, PCMK_ACTION_DEMOTE, pcmk__str_none) && pcmk__str_eq(then_task, PCMK_ACTION_STOP, pcmk__str_none)) { uint32_t flags = pe_order_optional; if (then_migratable) { /* A demote then B stop * -> A demote then B migrate_to */ pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_DEMOTE, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_TO, 0), NULL, flags, order->lh_rsc->cluster); // Order B migrate_from after A demote during partial migrations if (order->rh_rsc->partial_migration_target) { pcmk__new_ordering(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, PCMK_ACTION_DEMOTE, 0), NULL, order->rh_rsc, pcmk__op_key(order->rh_rsc->id, PCMK_ACTION_MIGRATE_FROM, 0), NULL, flags, order->lh_rsc->cluster); } } } free(first_task); free(then_task); }