LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_remote.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 231 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 12 0.0 %

          Line data    Source code
       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 <sys/param.h>
      13             : 
      14             : #include <crm/crm.h>
      15             : #include <crm/cib.h>
      16             : #include <crm/common/xml.h>
      17             : #include <crm/common/xml_internal.h>
      18             : 
      19             : #include <glib.h>
      20             : 
      21             : #include <crm/pengine/status.h>
      22             : #include <pacemaker-internal.h>
      23             : #include "libpacemaker_private.h"
      24             : 
      25             : enum remote_connection_state {
      26             :     remote_state_unknown = 0,
      27             :     remote_state_alive = 1,
      28             :     remote_state_resting = 2,
      29             :     remote_state_failed = 3,
      30             :     remote_state_stopped = 4
      31             : };
      32             : 
      33             : static const char *
      34           0 : state2text(enum remote_connection_state state)
      35             : {
      36           0 :     switch (state) {
      37           0 :         case remote_state_unknown:
      38           0 :             return "unknown";
      39           0 :         case remote_state_alive:
      40           0 :             return "alive";
      41           0 :         case remote_state_resting:
      42           0 :             return "resting";
      43           0 :         case remote_state_failed:
      44           0 :             return "failed";
      45           0 :         case remote_state_stopped:
      46           0 :             return "stopped";
      47             :     }
      48             : 
      49           0 :     return "impossible";
      50             : }
      51             : 
      52             : /* We always use pcmk__ar_guest_allowed with these convenience functions to
      53             :  * exempt internally generated constraints from the prohibition of user
      54             :  * constraints involving remote connection resources.
      55             :  *
      56             :  * The start ordering additionally uses pcmk__ar_unrunnable_first_blocks so that
      57             :  * the specified action is not runnable if the start is not runnable.
      58             :  */
      59             : 
      60             : static inline void
      61           0 : order_start_then_action(pcmk_resource_t *first_rsc, pcmk_action_t *then_action,
      62             :                         uint32_t extra)
      63             : {
      64           0 :     if ((first_rsc != NULL) && (then_action != NULL)) {
      65           0 :         pcmk__new_ordering(first_rsc, start_key(first_rsc), NULL,
      66             :                            then_action->rsc, NULL, then_action,
      67             :                            pcmk__ar_guest_allowed
      68             :                            |pcmk__ar_unrunnable_first_blocks
      69             :                            |extra,
      70             :                            first_rsc->cluster);
      71             :     }
      72           0 : }
      73             : 
      74             : static inline void
      75           0 : order_action_then_stop(pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
      76             :                        uint32_t extra)
      77             : {
      78           0 :     if ((first_action != NULL) && (then_rsc != NULL)) {
      79           0 :         pcmk__new_ordering(first_action->rsc, NULL, first_action,
      80           0 :                            then_rsc, stop_key(then_rsc), NULL,
      81             :                            pcmk__ar_guest_allowed|extra, then_rsc->cluster);
      82             :     }
      83           0 : }
      84             : 
      85             : static enum remote_connection_state
      86           0 : get_remote_node_state(const pcmk_node_t *node)
      87             : {
      88           0 :     const pcmk_resource_t *remote_rsc = NULL;
      89           0 :     const pcmk_node_t *cluster_node = NULL;
      90             : 
      91           0 :     CRM_ASSERT(node != NULL);
      92             : 
      93           0 :     remote_rsc = node->details->remote_rsc;
      94           0 :     CRM_ASSERT(remote_rsc != NULL);
      95             : 
      96           0 :     cluster_node = pcmk__current_node(remote_rsc);
      97             : 
      98             :     /* If the cluster node the remote connection resource resides on
      99             :      * is unclean or went offline, we can't process any operations
     100             :      * on that remote node until after it starts elsewhere.
     101             :      */
     102           0 :     if ((remote_rsc->next_role == pcmk_role_stopped)
     103           0 :         || (remote_rsc->allocated_to == NULL)) {
     104             : 
     105             :         // The connection resource is not going to run anywhere
     106             : 
     107           0 :         if ((cluster_node != NULL) && cluster_node->details->unclean) {
     108             :             /* The remote connection is failed because its resource is on a
     109             :              * failed node and can't be recovered elsewhere, so we must fence.
     110             :              */
     111           0 :             return remote_state_failed;
     112             :         }
     113             : 
     114           0 :         if (!pcmk_is_set(remote_rsc->flags, pcmk_rsc_failed)) {
     115             :             /* Connection resource is cleanly stopped */
     116           0 :             return remote_state_stopped;
     117             :         }
     118             : 
     119             :         /* Connection resource is failed */
     120             : 
     121           0 :         if ((remote_rsc->next_role == pcmk_role_stopped)
     122           0 :             && remote_rsc->remote_reconnect_ms
     123           0 :             && node->details->remote_was_fenced
     124           0 :             && !pe__shutdown_requested(node)) {
     125             : 
     126             :             /* We won't know whether the connection is recoverable until the
     127             :              * reconnect interval expires and we reattempt connection.
     128             :              */
     129           0 :             return remote_state_unknown;
     130             :         }
     131             : 
     132             :         /* The remote connection is in a failed state. If there are any
     133             :          * resources known to be active on it (stop) or in an unknown state
     134             :          * (probe), we must assume the worst and fence it.
     135             :          */
     136           0 :         return remote_state_failed;
     137             : 
     138           0 :     } else if (cluster_node == NULL) {
     139             :         /* Connection is recoverable but not currently running anywhere, so see
     140             :          * if we can recover it first
     141             :          */
     142           0 :         return remote_state_unknown;
     143             : 
     144           0 :     } else if (cluster_node->details->unclean
     145           0 :                || !(cluster_node->details->online)) {
     146             :         // Connection is running on a dead node, see if we can recover it first
     147           0 :         return remote_state_resting;
     148             : 
     149           0 :     } else if (pcmk__list_of_multiple(remote_rsc->running_on)
     150           0 :                && (remote_rsc->partial_migration_source != NULL)
     151           0 :                && (remote_rsc->partial_migration_target != NULL)) {
     152             :         /* We're in the middle of migrating a connection resource, so wait until
     153             :          * after the migration completes before performing any actions.
     154             :          */
     155           0 :         return remote_state_resting;
     156             : 
     157             :     }
     158           0 :     return remote_state_alive;
     159             : }
     160             : 
     161             : /*!
     162             :  * \internal
     163             :  * \brief Order actions on remote node relative to actions for the connection
     164             :  *
     165             :  * \param[in,out] action    An action scheduled on a Pacemaker Remote node
     166             :  */
     167             : static void
     168           0 : apply_remote_ordering(pcmk_action_t *action)
     169             : {
     170           0 :     pcmk_resource_t *remote_rsc = NULL;
     171           0 :     enum action_tasks task = pcmk_parse_action(action->task);
     172           0 :     enum remote_connection_state state = get_remote_node_state(action->node);
     173             : 
     174           0 :     uint32_t order_opts = pcmk__ar_none;
     175             : 
     176           0 :     if (action->rsc == NULL) {
     177           0 :         return;
     178             :     }
     179             : 
     180           0 :     CRM_ASSERT(pcmk__is_pacemaker_remote_node(action->node));
     181             : 
     182           0 :     remote_rsc = action->node->details->remote_rsc;
     183           0 :     CRM_ASSERT(remote_rsc != NULL);
     184             : 
     185           0 :     crm_trace("Order %s action %s relative to %s%s (state: %s)",
     186             :               action->task, action->uuid,
     187             :               pcmk_is_set(remote_rsc->flags, pcmk_rsc_failed)? "failed " : "",
     188             :               remote_rsc->id, state2text(state));
     189             : 
     190           0 :     if (pcmk__strcase_any_of(action->task, PCMK_ACTION_MIGRATE_TO,
     191             :                              PCMK_ACTION_MIGRATE_FROM, NULL)) {
     192             :         /* Migration ops map to pcmk_action_unspecified, but we need to apply
     193             :          * the same ordering as for stop or demote (see get_router_node()).
     194             :          */
     195           0 :         task = pcmk_action_stop;
     196             :     }
     197             : 
     198           0 :     switch (task) {
     199           0 :         case pcmk_action_start:
     200             :         case pcmk_action_promote:
     201           0 :             order_opts = pcmk__ar_none;
     202             : 
     203           0 :             if (state == remote_state_failed) {
     204             :                 /* Force recovery, by making this action required */
     205           0 :                 pcmk__set_relation_flags(order_opts,
     206             :                                          pcmk__ar_first_implies_then);
     207             :             }
     208             : 
     209             :             /* Ensure connection is up before running this action */
     210           0 :             order_start_then_action(remote_rsc, action, order_opts);
     211           0 :             break;
     212             : 
     213           0 :         case pcmk_action_stop:
     214           0 :             if (state == remote_state_alive) {
     215           0 :                 order_action_then_stop(action, remote_rsc,
     216             :                                        pcmk__ar_then_implies_first);
     217             : 
     218           0 :             } else if (state == remote_state_failed) {
     219             :                 /* The resource is active on the node, but since we don't have a
     220             :                  * valid connection, the only way to stop the resource is by
     221             :                  * fencing the node. There is no need to order the stop relative
     222             :                  * to the remote connection, since the stop will become implied
     223             :                  * by the fencing.
     224             :                  */
     225           0 :                 pe_fence_node(remote_rsc->cluster, action->node,
     226             :                               "resources are active but "
     227             :                               "connection is unrecoverable",
     228             :                               FALSE);
     229             : 
     230           0 :             } else if (remote_rsc->next_role == pcmk_role_stopped) {
     231             :                 /* State must be remote_state_unknown or remote_state_stopped.
     232             :                  * Since the connection is not coming back up in this
     233             :                  * transition, stop this resource first.
     234             :                  */
     235           0 :                 order_action_then_stop(action, remote_rsc,
     236             :                                        pcmk__ar_then_implies_first);
     237             : 
     238             :             } else {
     239             :                 /* The connection is going to be started somewhere else, so
     240             :                  * stop this resource after that completes.
     241             :                  */
     242           0 :                 order_start_then_action(remote_rsc, action, pcmk__ar_none);
     243             :             }
     244           0 :             break;
     245             : 
     246           0 :         case pcmk_action_demote:
     247             :             /* Only order this demote relative to the connection start if the
     248             :              * connection isn't being torn down. Otherwise, the demote would be
     249             :              * blocked because the connection start would not be allowed.
     250             :              */
     251           0 :             if ((state == remote_state_resting)
     252           0 :                 || (state == remote_state_unknown)) {
     253             : 
     254           0 :                 order_start_then_action(remote_rsc, action, pcmk__ar_none);
     255             :             } /* Otherwise we can rely on the stop ordering */
     256           0 :             break;
     257             : 
     258           0 :         default:
     259             :             /* Wait for the connection resource to be up */
     260           0 :             if (pcmk__action_is_recurring(action)) {
     261             :                 /* In case we ever get the recovery logic wrong, force
     262             :                  * recurring monitors to be restarted, even if just
     263             :                  * the connection was re-established
     264             :                  */
     265           0 :                 order_start_then_action(remote_rsc, action,
     266             :                                         pcmk__ar_first_implies_then);
     267             : 
     268             :             } else {
     269           0 :                 pcmk_node_t *cluster_node = pcmk__current_node(remote_rsc);
     270             : 
     271           0 :                 if ((task == pcmk_action_monitor) && (state == remote_state_failed)) {
     272             :                     /* We would only be here if we do not know the state of the
     273             :                      * resource on the remote node. Since we have no way to find
     274             :                      * out, it is necessary to fence the node.
     275             :                      */
     276           0 :                     pe_fence_node(remote_rsc->cluster, action->node,
     277             :                                   "resources are in unknown state "
     278             :                                   "and connection is unrecoverable", FALSE);
     279             :                 }
     280             : 
     281           0 :                 if ((cluster_node != NULL) && (state == remote_state_stopped)) {
     282             :                     /* The connection is currently up, but is going down
     283             :                      * permanently. Make sure we check services are actually
     284             :                      * stopped _before_ we let the connection get closed.
     285             :                      */
     286           0 :                     order_action_then_stop(action, remote_rsc,
     287             :                                            pcmk__ar_unrunnable_first_blocks);
     288             : 
     289             :                 } else {
     290           0 :                     order_start_then_action(remote_rsc, action, pcmk__ar_none);
     291             :                 }
     292             :             }
     293           0 :             break;
     294             :     }
     295             : }
     296             : 
     297             : static void
     298           0 : apply_container_ordering(pcmk_action_t *action)
     299             : {
     300             :     /* VMs are also classified as containers for these purposes... in
     301             :      * that they both involve a 'thing' running on a real or remote
     302             :      * cluster node.
     303             :      *
     304             :      * This allows us to be smarter about the type and extent of
     305             :      * recovery actions required in various scenarios
     306             :      */
     307           0 :     pcmk_resource_t *remote_rsc = NULL;
     308           0 :     pcmk_resource_t *container = NULL;
     309           0 :     enum action_tasks task = pcmk_parse_action(action->task);
     310             : 
     311           0 :     CRM_ASSERT(action->rsc != NULL);
     312           0 :     CRM_ASSERT(action->node != NULL);
     313           0 :     CRM_ASSERT(pcmk__is_pacemaker_remote_node(action->node));
     314             : 
     315           0 :     remote_rsc = action->node->details->remote_rsc;
     316           0 :     CRM_ASSERT(remote_rsc != NULL);
     317             : 
     318           0 :     container = remote_rsc->container;
     319           0 :     CRM_ASSERT(container != NULL);
     320             : 
     321           0 :     if (pcmk_is_set(container->flags, pcmk_rsc_failed)) {
     322           0 :         pe_fence_node(action->rsc->cluster, action->node, "container failed",
     323             :                       FALSE);
     324             :     }
     325             : 
     326           0 :     crm_trace("Order %s action %s relative to %s%s for %s%s",
     327             :               action->task, action->uuid,
     328             :               pcmk_is_set(remote_rsc->flags, pcmk_rsc_failed)? "failed " : "",
     329             :               remote_rsc->id,
     330             :               pcmk_is_set(container->flags, pcmk_rsc_failed)? "failed " : "",
     331             :               container->id);
     332             : 
     333           0 :     if (pcmk__strcase_any_of(action->task, PCMK_ACTION_MIGRATE_TO,
     334             :                              PCMK_ACTION_MIGRATE_FROM, NULL)) {
     335             :         /* Migration ops map to pcmk_action_unspecified, but we need to apply
     336             :          * the same ordering as for stop or demote (see get_router_node()).
     337             :          */
     338           0 :         task = pcmk_action_stop;
     339             :     }
     340             : 
     341           0 :     switch (task) {
     342           0 :         case pcmk_action_start:
     343             :         case pcmk_action_promote:
     344             :             // Force resource recovery if the container is recovered
     345           0 :             order_start_then_action(container, action,
     346             :                                     pcmk__ar_first_implies_then);
     347             : 
     348             :             // Wait for the connection resource to be up, too
     349           0 :             order_start_then_action(remote_rsc, action, pcmk__ar_none);
     350           0 :             break;
     351             : 
     352           0 :         case pcmk_action_stop:
     353             :         case pcmk_action_demote:
     354           0 :             if (pcmk_is_set(container->flags, pcmk_rsc_failed)) {
     355             :                 /* When the container representing a guest node fails, any stop
     356             :                  * or demote actions for resources running on the guest node
     357             :                  * are implied by the container stopping. This is similar to
     358             :                  * how fencing operations work for cluster nodes and remote
     359             :                  * nodes.
     360             :                  */
     361             :             } else {
     362             :                 /* Ensure the operation happens before the connection is brought
     363             :                  * down.
     364             :                  *
     365             :                  * If we really wanted to, we could order these after the
     366             :                  * connection start, IFF the container's current role was
     367             :                  * stopped (otherwise we re-introduce an ordering loop when the
     368             :                  * connection is restarting).
     369             :                  */
     370           0 :                 order_action_then_stop(action, remote_rsc, pcmk__ar_none);
     371             :             }
     372           0 :             break;
     373             : 
     374           0 :         default:
     375             :             /* Wait for the connection resource to be up */
     376           0 :             if (pcmk__action_is_recurring(action)) {
     377             :                 /* In case we ever get the recovery logic wrong, force
     378             :                  * recurring monitors to be restarted, even if just
     379             :                  * the connection was re-established
     380             :                  */
     381           0 :                 if (task != pcmk_action_unspecified) {
     382           0 :                     order_start_then_action(remote_rsc, action,
     383             :                                             pcmk__ar_first_implies_then);
     384             :                 }
     385             :             } else {
     386           0 :                 order_start_then_action(remote_rsc, action, pcmk__ar_none);
     387             :             }
     388           0 :             break;
     389             :     }
     390           0 : }
     391             : 
     392             : /*!
     393             :  * \internal
     394             :  * \brief Order all relevant actions relative to remote connection actions
     395             :  *
     396             :  * \param[in,out] scheduler  Scheduler data
     397             :  */
     398             : void
     399           0 : pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler)
     400             : {
     401           0 :     if (!pcmk_is_set(scheduler->flags, pcmk_sched_have_remote_nodes)) {
     402           0 :         return;
     403             :     }
     404             : 
     405           0 :     crm_trace("Creating remote connection orderings");
     406             : 
     407           0 :     for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
     408           0 :         pcmk_action_t *action = iter->data;
     409           0 :         pcmk_resource_t *remote = NULL;
     410             : 
     411             :         // We are only interested in resource actions
     412           0 :         if (action->rsc == NULL) {
     413           0 :             continue;
     414             :         }
     415             : 
     416             :         /* Special case: If we are clearing the failcount of an actual
     417             :          * remote connection resource, then make sure this happens before
     418             :          * any start of the resource in this transition.
     419             :          */
     420           0 :         if (action->rsc->is_remote_node &&
     421           0 :             pcmk__str_eq(action->task, PCMK_ACTION_CLEAR_FAILCOUNT,
     422             :                          pcmk__str_none)) {
     423             : 
     424           0 :             pcmk__new_ordering(action->rsc, NULL, action, action->rsc,
     425           0 :                                pcmk__op_key(action->rsc->id, PCMK_ACTION_START,
     426             :                                             0),
     427             :                                NULL, pcmk__ar_ordered, scheduler);
     428             : 
     429           0 :             continue;
     430             :         }
     431             : 
     432             :         // We are only interested in actions assigned to a node
     433           0 :         if (action->node == NULL) {
     434           0 :             continue;
     435             :         }
     436             : 
     437           0 :         if (!pcmk__is_pacemaker_remote_node(action->node)) {
     438           0 :             continue;
     439             :         }
     440             : 
     441             :         /* We are only interested in real actions.
     442             :          *
     443             :          * @TODO This is probably wrong; pseudo-actions might be converted to
     444             :          * real actions and vice versa later in update_actions() at the end of
     445             :          * pcmk__apply_orderings().
     446             :          */
     447           0 :         if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
     448           0 :             continue;
     449             :         }
     450             : 
     451           0 :         remote = action->node->details->remote_rsc;
     452           0 :         if (remote == NULL) {
     453             :             // Orphaned
     454           0 :             continue;
     455             :         }
     456             : 
     457             :         /* Another special case: if a resource is moving to a Pacemaker Remote
     458             :          * node, order the stop on the original node after any start of the
     459             :          * remote connection. This ensures that if the connection fails to
     460             :          * start, we leave the resource running on the original node.
     461             :          */
     462           0 :         if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none)) {
     463           0 :             for (GList *item = action->rsc->actions; item != NULL;
     464           0 :                  item = item->next) {
     465           0 :                 pcmk_action_t *rsc_action = item->data;
     466             : 
     467           0 :                 if (!pcmk__same_node(rsc_action->node, action->node)
     468           0 :                     && pcmk__str_eq(rsc_action->task, PCMK_ACTION_STOP,
     469             :                                     pcmk__str_none)) {
     470           0 :                     pcmk__new_ordering(remote, start_key(remote), NULL,
     471             :                                        action->rsc, NULL, rsc_action,
     472             :                                        pcmk__ar_ordered, scheduler);
     473             :                 }
     474             :             }
     475             :         }
     476             : 
     477             :         /* The action occurs across a remote connection, so create
     478             :          * ordering constraints that guarantee the action occurs while the node
     479             :          * is active (after start, before stop ... things like that).
     480             :          *
     481             :          * This is somewhat brittle in that we need to make sure the results of
     482             :          * this ordering are compatible with the result of get_router_node().
     483             :          * It would probably be better to add PCMK__XA_ROUTER_NODE as part of
     484             :          * this logic rather than create_graph_action().
     485             :          */
     486           0 :         if (remote->container) {
     487           0 :             crm_trace("Container ordering for %s", action->uuid);
     488           0 :             apply_container_ordering(action);
     489             : 
     490             :         } else {
     491           0 :             crm_trace("Remote ordering for %s", action->uuid);
     492           0 :             apply_remote_ordering(action);
     493             :         }
     494             :     }
     495             : }
     496             : 
     497             : /*!
     498             :  * \internal
     499             :  * \brief Check whether a node is a failed remote node
     500             :  *
     501             :  * \param[in] node  Node to check
     502             :  *
     503             :  * \return true if \p node is a failed remote node, false otherwise
     504             :  */
     505             : bool
     506           0 : pcmk__is_failed_remote_node(const pcmk_node_t *node)
     507             : {
     508           0 :     return pcmk__is_remote_node(node) && (node->details->remote_rsc != NULL)
     509           0 :            && (get_remote_node_state(node) == remote_state_failed);
     510             : }
     511             : 
     512             : /*!
     513             :  * \internal
     514             :  * \brief Check whether a given resource corresponds to a given node as guest
     515             :  *
     516             :  * \param[in] rsc   Resource to check
     517             :  * \param[in] node  Node to check
     518             :  *
     519             :  * \return true if \p node is a guest node and \p rsc is its containing
     520             :  *         resource, otherwise false
     521             :  */
     522             : bool
     523           0 : pcmk__rsc_corresponds_to_guest(const pcmk_resource_t *rsc,
     524             :                                const pcmk_node_t *node)
     525             : {
     526           0 :     return (rsc != NULL) && (rsc->fillers != NULL) && (node != NULL)
     527           0 :             && (node->details->remote_rsc != NULL)
     528           0 :             && (node->details->remote_rsc->container == rsc);
     529             : }
     530             : 
     531             : /*!
     532             :  * \internal
     533             :  * \brief Get proper connection host that a remote action must be routed through
     534             :  *
     535             :  * A remote connection resource might be starting, stopping, or migrating in the
     536             :  * same transition that an action needs to be executed on its Pacemaker Remote
     537             :  * node. Determine the proper node that the remote action should be routed
     538             :  * through.
     539             :  *
     540             :  * \param[in] action  (Potentially remote) action to route
     541             :  *
     542             :  * \return Connection host that action should be routed through if remote,
     543             :  *         otherwise NULL
     544             :  */
     545             : pcmk_node_t *
     546           0 : pcmk__connection_host_for_action(const pcmk_action_t *action)
     547             : {
     548           0 :     pcmk_node_t *began_on = NULL;
     549           0 :     pcmk_node_t *ended_on = NULL;
     550           0 :     bool partial_migration = false;
     551           0 :     const char *task = action->task;
     552             : 
     553           0 :     if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_none)
     554           0 :         || !pcmk__is_pacemaker_remote_node(action->node)) {
     555           0 :         return NULL;
     556             :     }
     557             : 
     558           0 :     CRM_ASSERT(action->node->details->remote_rsc != NULL);
     559             : 
     560           0 :     began_on = pcmk__current_node(action->node->details->remote_rsc);
     561           0 :     ended_on = action->node->details->remote_rsc->allocated_to;
     562           0 :     if (action->node->details->remote_rsc
     563           0 :         && (action->node->details->remote_rsc->container == NULL)
     564           0 :         && action->node->details->remote_rsc->partial_migration_target) {
     565           0 :         partial_migration = true;
     566             :     }
     567             : 
     568           0 :     if (began_on == NULL) {
     569           0 :         crm_trace("Routing %s for %s through remote connection's "
     570             :                   "next node %s (starting)%s",
     571             :                   action->task, (action->rsc? action->rsc->id : "no resource"),
     572             :                   (ended_on? ended_on->details->uname : "none"),
     573             :                   partial_migration? " (partial migration)" : "");
     574           0 :         return ended_on;
     575             :     }
     576             : 
     577           0 :     if (ended_on == NULL) {
     578           0 :         crm_trace("Routing %s for %s through remote connection's "
     579             :                   "current node %s (stopping)%s",
     580             :                   action->task, (action->rsc? action->rsc->id : "no resource"),
     581             :                   (began_on? began_on->details->uname : "none"),
     582             :                   partial_migration? " (partial migration)" : "");
     583           0 :         return began_on;
     584             :     }
     585             : 
     586           0 :     if (pcmk__same_node(began_on, ended_on)) {
     587           0 :         crm_trace("Routing %s for %s through remote connection's "
     588             :                   "current node %s (not moving)%s",
     589             :                   action->task, (action->rsc? action->rsc->id : "no resource"),
     590             :                   (began_on? began_on->details->uname : "none"),
     591             :                   partial_migration? " (partial migration)" : "");
     592           0 :         return began_on;
     593             :     }
     594             : 
     595             :     /* If we get here, the remote connection is moving during this transition.
     596             :      * This means some actions for resources behind the connection will get
     597             :      * routed through the cluster node the connection resource is currently on,
     598             :      * and others are routed through the cluster node the connection will end up
     599             :      * on.
     600             :      */
     601             : 
     602           0 :     if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
     603           0 :         task = g_hash_table_lookup(action->meta, "notify_operation");
     604             :     }
     605             : 
     606             :     /*
     607             :      * Stop, demote, and migration actions must occur before the connection can
     608             :      * move (these actions are required before the remote resource can stop). In
     609             :      * this case, we know these actions have to be routed through the initial
     610             :      * cluster node the connection resource lived on before the move takes
     611             :      * place.
     612             :      *
     613             :      * The exception is a partial migration of a (non-guest) remote connection
     614             :      * resource; in that case, all actions (even these) will be ordered after
     615             :      * the connection's pseudo-start on the migration target, so the target is
     616             :      * the router node.
     617             :      */
     618           0 :     if (pcmk__strcase_any_of(task, PCMK_ACTION_CANCEL, PCMK_ACTION_STOP,
     619             :                              PCMK_ACTION_DEMOTE, PCMK_ACTION_MIGRATE_FROM,
     620             :                              PCMK_ACTION_MIGRATE_TO, NULL)
     621           0 :         && !partial_migration) {
     622           0 :         crm_trace("Routing %s for %s through remote connection's "
     623             :                   "current node %s (moving)%s",
     624             :                   action->task, (action->rsc? action->rsc->id : "no resource"),
     625             :                   (began_on? began_on->details->uname : "none"),
     626             :                   partial_migration? " (partial migration)" : "");
     627           0 :         return began_on;
     628             :     }
     629             : 
     630             :     /* Everything else (start, promote, monitor, probe, refresh,
     631             :      * clear failcount, delete, ...) must occur after the connection starts on
     632             :      * the node it is moving to.
     633             :      */
     634           0 :     crm_trace("Routing %s for %s through remote connection's "
     635             :               "next node %s (moving)%s",
     636             :               action->task, (action->rsc? action->rsc->id : "no resource"),
     637             :               (ended_on? ended_on->details->uname : "none"),
     638             :               partial_migration? " (partial migration)" : "");
     639           0 :     return ended_on;
     640             : }
     641             : 
     642             : /*!
     643             :  * \internal
     644             :  * \brief Replace remote connection's addr="#uname" with actual address
     645             :  *
     646             :  * REMOTE_CONTAINER_HACK: If a given resource is a remote connection resource
     647             :  * with its "addr" parameter set to "#uname", pull the actual value from the
     648             :  * parameters evaluated without a node (which was put there earlier in
     649             :  * pcmk__create_graph() when the bundle's expand() method was called).
     650             :  *
     651             :  * \param[in,out] rsc     Resource to check
     652             :  * \param[in,out] params  Resource parameters evaluated per node
     653             :  */
     654             : void
     655           0 : pcmk__substitute_remote_addr(pcmk_resource_t *rsc, GHashTable *params)
     656             : {
     657           0 :     const char *remote_addr = g_hash_table_lookup(params, PCMK_REMOTE_RA_ADDR);
     658             : 
     659           0 :     if (pcmk__str_eq(remote_addr, "#uname", pcmk__str_none)) {
     660           0 :         GHashTable *base = pe_rsc_params(rsc, NULL, rsc->cluster);
     661             : 
     662           0 :         remote_addr = g_hash_table_lookup(base, PCMK_REMOTE_RA_ADDR);
     663           0 :         if (remote_addr != NULL) {
     664           0 :             pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, remote_addr);
     665             :         }
     666             :     }
     667           0 : }
     668             : 
     669             : /*!
     670             :  * \brief Add special guest node meta-attributes to XML
     671             :  *
     672             :  * If a given action will be executed on a guest node, add the following as XML
     673             :  * attributes (using meta-attribute naming):
     674             :  * * The resource's \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET meta-attribute
     675             :  *   (usually set only for bundles), as \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET
     676             :  * * The guest's physical host (current host for "down" actions, next host for
     677             :  *   "up" actions), as \c PCMK__META_PHYSICAL_HOST
     678             :  *
     679             :  * If the guest node has no physical host, then don't add either attribute.
     680             :  *
     681             :  * \param[in,out] args_xml  XML to add attributes to
     682             :  * \param[in]     action    Action to check
     683             :  */
     684             : void
     685           0 : pcmk__add_guest_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action)
     686             : {
     687           0 :     const pcmk_node_t *guest = action->node;
     688           0 :     const pcmk_node_t *host = NULL;
     689             :     enum action_tasks task;
     690             : 
     691           0 :     if (!pcmk__is_guest_or_bundle_node(guest)) {
     692           0 :         return;
     693             :     }
     694             : 
     695           0 :     task = pcmk_parse_action(action->task);
     696           0 :     if ((task == pcmk_action_notify) || (task == pcmk_action_notified)) {
     697           0 :         task = pcmk_parse_action(g_hash_table_lookup(action->meta,
     698             :                                                      "notify_operation"));
     699             :     }
     700             : 
     701           0 :     switch (task) {
     702           0 :         case pcmk_action_stop:
     703             :         case pcmk_action_stopped:
     704             :         case pcmk_action_demote:
     705             :         case pcmk_action_demoted:
     706             :             // "Down" actions take place on guest's current host
     707           0 :             host = pcmk__current_node(guest->details->remote_rsc->container);
     708           0 :             break;
     709             : 
     710           0 :         case pcmk_action_start:
     711             :         case pcmk_action_started:
     712             :         case pcmk_action_monitor:
     713             :         case pcmk_action_promote:
     714             :         case pcmk_action_promoted:
     715             :             // "Up" actions take place on guest's next host
     716           0 :             host = guest->details->remote_rsc->container->allocated_to;
     717           0 :             break;
     718             : 
     719           0 :         default:
     720           0 :             break;
     721             :     }
     722             : 
     723           0 :     if (host != NULL) {
     724             :         gpointer target =
     725           0 :             g_hash_table_lookup(action->rsc->meta,
     726             :                                 PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
     727             : 
     728           0 :         hash2metafield((gpointer) PCMK_META_CONTAINER_ATTRIBUTE_TARGET,
     729             :                        target,
     730             :                        (gpointer) args_xml);
     731           0 :         hash2metafield((gpointer) PCMK__META_PHYSICAL_HOST,
     732           0 :                        (gpointer) host->details->uname,
     733             :                        (gpointer) args_xml);
     734             :     }
     735             : }

Generated by: LCOV version 1.14