pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pcmk_sched_migration.c
Go to the documentation of this file.
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 
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->details->uname);
33 
35  target->details->uname);
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 
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,
63  PCMK_ACTION_MIGRATE_TO, current, TRUE,
64  rsc->cluster);
65  }
66  migrate_from = custom_action(rsc, pcmk__op_key(rsc->id,
69  TRUE, rsc->cluster);
70 
73 
74  // This is easier than trying to delete it from the graph
76 
77  if (rsc->partial_migration_target == NULL) {
80  migrate_to->needs = start->needs;
81 
82  // Probe -> migrate_to -> migrate_from
84  NULL,
85  rsc,
87  NULL, pcmk__ar_ordered, rsc->cluster);
89  NULL,
90  rsc,
92  NULL,
94  rsc->cluster);
95  } else {
97  migrate_from->needs = start->needs;
98 
99  // Probe -> migrate_from (migrate_to already completed)
101  NULL,
102  rsc,
104  NULL, pcmk__ar_ordered, rsc->cluster);
105  }
106 
107  // migrate_from before stop or start
109  NULL,
110  rsc, pcmk__op_key(rsc->id, PCMK_ACTION_STOP, 0),
111  NULL,
113  rsc->cluster);
115  NULL,
116  rsc, pcmk__op_key(rsc->id, PCMK_ACTION_START, 0),
117  NULL,
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 
152 void
153 pcmk__abort_dangling_migration(void *data, void *user_data)
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,
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);
168  if (cleanup) {
169  pcmk__schedule_cleanup(rsc, dangling_source, false);
170  }
171 }
172 
182 bool
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 
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 
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 = 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 
262 void
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,
302  NULL, order->rh_rsc,
303  pcmk__op_key(order->rh_rsc->id,
305  NULL, flags, order->lh_rsc->cluster);
306  }
307 
308  if (then_migratable) {
309  if (first_migratable) {
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,
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) {
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,
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,
351  0),
352  NULL, order->rh_rsc,
353  pcmk__op_key(order->rh_rsc->id,
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,
369  NULL, order->rh_rsc,
370  pcmk__op_key(order->rh_rsc->id,
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,
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,
399  NULL, flags, order->lh_rsc->cluster);
400  }
401  }
402  }
403 
404  free(first_task);
405  free(then_task);
406 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
pcmk_action_t * lh_action
Definition: internal.h:172
enum rsc_start_requirement needs
Prerequisite for recovery.
Definition: actions.h:411
void pcmk__order_migration_equivalents(pe__ordering_t *order)
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition: resources.h:412
G_GNUC_INTERNAL void pcmk__schedule_cleanup(pcmk_resource_t *rsc, const pcmk_node_t *node, bool optional)
char data[0]
Definition: cpg.c:55
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:76
pcmk_node_t * partial_migration_target
The destination node, if migrate_to completed but migrate_from has not.
Definition: resources.h:454
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:379
pcmk_action_t * rh_action
Definition: internal.h:177
If &#39;first&#39; is unrunnable, &#39;then&#39; becomes a real, unmigratable action.
Implementation of pcmk_action_t.
Definition: actions.h:390
#define PCMK_ACTION_MONITOR
Definition: actions.h:59
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:58
void pcmk__create_migration_actions(pcmk_resource_t *rsc, const pcmk_node_t *current)
const char * action
Definition: pcmk_fence.c:30
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
Implementation of pcmk_resource_t.
Definition: resources.h:399
Actions are ordered (optionally, if no other flags are set)
Whether resource is considered failed.
Definition: resources.h:151
GHashTable * meta
Meta-attributes relevant to action.
Definition: actions.h:414
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
struct pe_node_shared_s * details
Basic node information.
Definition: nodes.h:134
#define PCMK_ACTION_START
Definition: actions.h:71
bool pcmk__rsc_can_migrate(const pcmk_resource_t *rsc, const pcmk_node_t *current)
unsigned long long flags
Group of enum pcmk_rsc_flags.
Definition: resources.h:429
const char * uname
Node name in cluster.
Definition: nodes.h:68
#define PCMK_ACTION_STOP
Definition: actions.h:74
#define XML_LRM_ATTR_MIGRATE_TARGET
Definition: msg_xml.h:331
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:42
Implementation of pcmk_node_t.
Definition: nodes.h:130
pcmk_resource_t * lh_rsc
Definition: internal.h:171
Whether resource has pending start action in history.
Definition: resources.h:160
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:1117
const char * target
Definition: pcmk_fence.c:29
gboolean is_remote_node
Whether this is a remote connection.
Definition: resources.h:432
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition: common.c:508
#define start_action(rsc, node, optional)
Definition: internal.h:385
Whether action does not require invoking an agent.
Definition: actions.h:238
#define CRM_ASSERT(expr)
Definition: results.h:42
Relation applies only if &#39;first&#39; cannot be part of a live migration.
Whether resource is allowed to live-migrate.
Definition: resources.h:172
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:57
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:96
pcmk_node_t * allocated_to
Node resource is assigned to.
Definition: resources.h:451
gboolean is_parent(pcmk_resource_t *child, pcmk_resource_t *rsc)
Definition: complex.c:919
#define PCMK_ACTION_PROMOTE
Definition: actions.h:65
#define XML_OP_ATTR_PENDING
Definition: msg_xml.h:272
Whether action is allowed to be part of a live migration.
Definition: actions.h:253
Whether action is a stop to abort a dangling migration.
Definition: actions.h:259
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:37
#define pe__set_order_flags(order_flags, flags_to_set)
Definition: internal.h:128
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
#define XML_LRM_ATTR_MIGRATE_SOURCE
Definition: msg_xml.h:330
Whether resource is managed.
Definition: resources.h:106
pcmk_resource_t * rh_rsc
Definition: internal.h:176
gboolean unclean
Whether node requires fencing.
Definition: nodes.h:76
uint64_t flags
Definition: remote.c:215
void pcmk__abort_dangling_migration(void *data, void *user_data)
char * id
Resource ID in configuration.
Definition: resources.h:400