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