pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_sched_migration.c
Go to the documentation of this file.
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 
27 static void
28 add_migration_meta(pcmk_action_t *action, const pcmk_node_t *source,
29  const pcmk_node_t *target)
30 {
32  source->priv->name);
33 
35  target->priv->name);
36 }
37 
45 void
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,
64  PCMK_ACTION_MIGRATE_TO, current, TRUE,
65  rsc->priv->scheduler);
66  }
67  migrate_from = custom_action(rsc, pcmk__op_key(rsc->id,
70  rsc->priv->assigned_node, TRUE,
71  rsc->priv->scheduler);
72 
75 
76  // This is easier than trying to delete it from the graph
78 
79  if (target_node == NULL) {
82  migrate_to->needs = start->needs;
83 
84  // Probe -> migrate_to -> migrate_from
86  NULL,
87  rsc,
89  NULL, pcmk__ar_ordered, rsc->priv->scheduler);
91  NULL,
92  rsc,
94  NULL,
96  rsc->priv->scheduler);
97  } else {
99  migrate_from->needs = start->needs;
100 
101  // Probe -> migrate_from (migrate_to already completed)
103  NULL,
104  rsc,
106  NULL, pcmk__ar_ordered, rsc->priv->scheduler);
107  }
108 
109  // migrate_from before stop or start
111  NULL,
112  rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0),
113  NULL,
115  rsc->priv->scheduler);
117  NULL,
118  rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0),
119  NULL,
123  rsc->priv->scheduler);
124 
125  if (migrate_to != NULL) {
126  add_migration_meta(migrate_to, current, rsc->priv->assigned_node);
127 
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,
142  }
143  }
144 
145  add_migration_meta(migrate_from, current, rsc->priv->assigned_node);
146 }
147 
155 void
156 pcmk__abort_dangling_migration(void *data, void *user_data)
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);
168 }
169 
179 bool
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 
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 
238 static char *
239 task_from_action_or_key(const pcmk_action_t *action, const char *key)
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 
261 void
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,
299  NULL, order->rsc2,
300  pcmk__op_key(order->rsc2->id,
302  NULL, flags, order->rsc1->priv->scheduler);
303  }
304 
305  if (then_migratable) {
306  if (first_migratable) {
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,
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) {
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,
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,
348  0),
349  NULL, order->rsc2,
350  pcmk__op_key(order->rsc2->id,
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,
366  NULL, order->rsc2,
367  pcmk__op_key(order->rsc2->id,
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,
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,
396  NULL, flags,
397  order->rsc1->priv->scheduler);
398  }
399  }
400  }
401 
402  free(first_task);
403  free(then_task);
404 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
char data[0]
Definition: cpg.c:58
#define PCMK_META_RECORD_PENDING
Definition: options.h:104
G_GNUC_INTERNAL void pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_task, pcmk_action_t *first_action, pcmk_resource_t *then_rsc, char *then_task, pcmk_action_t *then_action, uint32_t flags, pcmk_scheduler_t *sched)
#define stop_action(rsc, node, optional)
Definition: internal.h:206
#define pcmk__rsc_trace(rsc, fmt, args...)
If &#39;first&#39; is unrunnable, &#39;then&#39; becomes a real, unmigratable action.
#define pcmk__insert_meta(obj, name, value)
#define PCMK_ACTION_MONITOR
Definition: actions.h:51
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:50
gboolean unclean
Definition: nodes.h:58
void pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current)
#define pcmk__set_relation_flags(ar_flags, flags_to_set)
const char * action
Definition: pcmk_fence.c:32
#define PCMK__META_MIGRATE_TARGET
pcmk__node_private_t * priv
Definition: nodes.h:85
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
pcmk_scheduler_t * scheduler
Actions are ordered (optionally, if no other flags are set)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
#define PCMK_ACTION_START
Definition: actions.h:63
pcmk__resource_private_t * priv
Definition: resources.h:61
bool pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current)
Wrappers for and extensions to libxml2.
#define PCMK_ACTION_STOP
Definition: actions.h:66
enum pcmk__requires needs
#define PCMK_VALUE_TRUE
Definition: options.h:218
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: actions.c:195
#define pcmk__str_copy(str)
pcmk_action_t * custom_action(pcmk_resource_t *rsc, char *key, const char *task, const pcmk_node_t *on_node, gboolean optional, pcmk_scheduler_t *scheduler)
Create or update an action object.
Definition: pe_actions.c:1093
const char * target
Definition: pcmk_fence.c:31
#define start_action(rsc, node, optional)
Definition: internal.h:210
void pcmk__order_migration_equivalents(pcmk__action_relation_t *order)
Relation applies only if &#39;first&#39; cannot be part of a live migration.
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:49
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:248
gboolean is_parent(pcmk_resource_t *child, pcmk_resource_t *rsc)
Definition: complex.c:1000
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
#define pcmk__set_action_flags(action, flags_to_set)
unsigned long long flags
Definition: resources.h:69
#define PCMK__META_MIGRATE_SOURCE
struct pcmk__node_details * details
Definition: nodes.h:82
uint64_t flags
Definition: remote.c:211
void pcmk__abort_dangling_migration(void *data, void *user_data)
pcmk_node_t * partial_migration_target