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 <stdio.h>
13 : #include <sys/param.h>
14 : #include <glib.h>
15 :
16 : #include <crm/lrmd_internal.h>
17 : #include <crm/common/scheduler_internal.h>
18 : #include <pacemaker-internal.h>
19 : #include "libpacemaker_private.h"
20 :
21 : /*!
22 : * \internal
23 : * \brief Get the action flags relevant to ordering constraints
24 : *
25 : * \param[in,out] action Action to check
26 : * \param[in] node Node that *other* action in the ordering is on
27 : * (used only for clone resource actions)
28 : *
29 : * \return Action flags that should be used for orderings
30 : */
31 : static uint32_t
32 0 : action_flags_for_ordering(pcmk_action_t *action, const pcmk_node_t *node)
33 : {
34 0 : bool runnable = false;
35 : uint32_t flags;
36 :
37 : // For non-resource actions, return the action flags
38 0 : if (action->rsc == NULL) {
39 0 : return action->flags;
40 : }
41 :
42 : /* For non-clone resources, or a clone action not assigned to a node,
43 : * return the flags as determined by the resource method without a node
44 : * specified.
45 : */
46 0 : flags = action->rsc->cmds->action_flags(action, NULL);
47 0 : if ((node == NULL) || !pcmk__is_clone(action->rsc)) {
48 0 : return flags;
49 : }
50 :
51 : /* Otherwise (i.e., for clone resource actions on a specific node), first
52 : * remember whether the non-node-specific action is runnable.
53 : */
54 0 : runnable = pcmk_is_set(flags, pcmk_action_runnable);
55 :
56 : // Then recheck the resource method with the node
57 0 : flags = action->rsc->cmds->action_flags(action, node);
58 :
59 : /* For clones in ordering constraints, the node-specific "runnable" doesn't
60 : * matter, just the non-node-specific setting (i.e., is the action runnable
61 : * anywhere).
62 : *
63 : * This applies only to runnable, and only for ordering constraints. This
64 : * function shouldn't be used for other types of constraints without
65 : * changes. Not very satisfying, but it's logical and appears to work well.
66 : */
67 0 : if (runnable && !pcmk_is_set(flags, pcmk_action_runnable)) {
68 0 : pcmk__set_raw_action_flags(flags, action->rsc->id,
69 : pcmk_action_runnable);
70 : }
71 0 : return flags;
72 : }
73 :
74 : /*!
75 : * \internal
76 : * \brief Get action UUID that should be used with a resource ordering
77 : *
78 : * When an action is ordered relative to an action for a collective resource
79 : * (clone, group, or bundle), it actually needs to be ordered after all
80 : * instances of the collective have completed the relevant action (for example,
81 : * given "start CLONE then start RSC", RSC must wait until all instances of
82 : * CLONE have started). Given the UUID and resource of the first action in an
83 : * ordering, this returns the UUID of the action that should actually be used
84 : * for ordering (for example, "CLONE_started_0" instead of "CLONE_start_0").
85 : *
86 : * \param[in] first_uuid UUID of first action in ordering
87 : * \param[in] first_rsc Resource of first action in ordering
88 : *
89 : * \return Newly allocated copy of UUID to use with ordering
90 : * \note It is the caller's responsibility to free the return value.
91 : */
92 : static char *
93 0 : action_uuid_for_ordering(const char *first_uuid,
94 : const pcmk_resource_t *first_rsc)
95 : {
96 0 : guint interval_ms = 0;
97 0 : char *uuid = NULL;
98 0 : char *rid = NULL;
99 0 : char *first_task_str = NULL;
100 0 : enum action_tasks first_task = pcmk_action_unspecified;
101 0 : enum action_tasks remapped_task = pcmk_action_unspecified;
102 :
103 : // Only non-notify actions for collective resources need remapping
104 0 : if ((strstr(first_uuid, PCMK_ACTION_NOTIFY) != NULL)
105 0 : || (first_rsc->variant < pcmk_rsc_variant_group)) {
106 0 : goto done;
107 : }
108 :
109 : // Only non-recurring actions need remapping
110 0 : CRM_ASSERT(parse_op_key(first_uuid, &rid, &first_task_str, &interval_ms));
111 0 : if (interval_ms > 0) {
112 0 : goto done;
113 : }
114 :
115 0 : first_task = pcmk_parse_action(first_task_str);
116 0 : switch (first_task) {
117 0 : case pcmk_action_stop:
118 : case pcmk_action_start:
119 : case pcmk_action_notify:
120 : case pcmk_action_promote:
121 : case pcmk_action_demote:
122 0 : remapped_task = first_task + 1;
123 0 : break;
124 0 : case pcmk_action_stopped:
125 : case pcmk_action_started:
126 : case pcmk_action_notified:
127 : case pcmk_action_promoted:
128 : case pcmk_action_demoted:
129 0 : remapped_task = first_task;
130 0 : break;
131 0 : case pcmk_action_monitor:
132 : case pcmk_action_shutdown:
133 : case pcmk_action_fence:
134 0 : break;
135 0 : default:
136 0 : crm_err("Unknown action '%s' in ordering", first_task_str);
137 0 : break;
138 : }
139 :
140 0 : if (remapped_task != pcmk_action_unspecified) {
141 : /* If a clone or bundle has notifications enabled, the ordering will be
142 : * relative to when notifications have been sent for the remapped task.
143 : */
144 0 : if (pcmk_is_set(first_rsc->flags, pcmk_rsc_notify)
145 0 : && (pcmk__is_clone(first_rsc) || pcmk__is_bundled(first_rsc))) {
146 0 : uuid = pcmk__notify_key(rid, "confirmed-post",
147 : pcmk_action_text(remapped_task));
148 : } else {
149 0 : uuid = pcmk__op_key(rid, pcmk_action_text(remapped_task), 0);
150 : }
151 0 : pcmk__rsc_trace(first_rsc,
152 : "Remapped action UUID %s to %s for ordering purposes",
153 : first_uuid, uuid);
154 : }
155 :
156 0 : done:
157 0 : free(first_task_str);
158 0 : free(rid);
159 0 : return (uuid != NULL)? uuid : pcmk__str_copy(first_uuid);
160 : }
161 :
162 : /*!
163 : * \internal
164 : * \brief Get actual action that should be used with an ordering
165 : *
166 : * When an action is ordered relative to an action for a collective resource
167 : * (clone, group, or bundle), it actually needs to be ordered after all
168 : * instances of the collective have completed the relevant action (for example,
169 : * given "start CLONE then start RSC", RSC must wait until all instances of
170 : * CLONE have started). Given the first action in an ordering, this returns the
171 : * the action that should actually be used for ordering (for example, the
172 : * started action instead of the start action).
173 : *
174 : * \param[in] action First action in an ordering
175 : *
176 : * \return Actual action that should be used for the ordering
177 : */
178 : static pcmk_action_t *
179 0 : action_for_ordering(pcmk_action_t *action)
180 : {
181 0 : pcmk_action_t *result = action;
182 0 : pcmk_resource_t *rsc = action->rsc;
183 :
184 0 : if ((rsc != NULL) && (rsc->variant >= pcmk_rsc_variant_group)
185 0 : && (action->uuid != NULL)) {
186 0 : char *uuid = action_uuid_for_ordering(action->uuid, rsc);
187 :
188 0 : result = find_first_action(rsc->actions, uuid, NULL, NULL);
189 0 : if (result == NULL) {
190 0 : crm_warn("Not remapping %s to %s because %s does not have "
191 : "remapped action", action->uuid, uuid, rsc->id);
192 0 : result = action;
193 : }
194 0 : free(uuid);
195 : }
196 0 : return result;
197 : }
198 :
199 : /*!
200 : * \internal
201 : * \brief Wrapper for update_ordered_actions() method for readability
202 : *
203 : * \param[in,out] rsc Resource to call method for
204 : * \param[in,out] first 'First' action in an ordering
205 : * \param[in,out] then 'Then' action in an ordering
206 : * \param[in] node If not NULL, limit scope of ordering to this
207 : * node (only used when interleaving instances)
208 : * \param[in] flags Action flags for \p first for ordering purposes
209 : * \param[in] filter Action flags to limit scope of certain updates
210 : * (may include pcmk_action_optional to affect only
211 : * mandatory actions, and pe_action_runnable to
212 : * affect only runnable actions)
213 : * \param[in] type Group of enum pcmk__action_relation_flags to apply
214 : * \param[in,out] scheduler Scheduler data
215 : *
216 : * \return Group of enum pcmk__updated flags indicating what was updated
217 : */
218 : static inline uint32_t
219 0 : update(pcmk_resource_t *rsc, pcmk_action_t *first, pcmk_action_t *then,
220 : const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type,
221 : pcmk_scheduler_t *scheduler)
222 : {
223 0 : return rsc->cmds->update_ordered_actions(first, then, node, flags, filter,
224 : type, scheduler);
225 : }
226 :
227 : /*!
228 : * \internal
229 : * \brief Update flags for ordering's actions appropriately for ordering's flags
230 : *
231 : * \param[in,out] first First action in an ordering
232 : * \param[in,out] then Then action in an ordering
233 : * \param[in] first_flags Action flags for \p first for ordering purposes
234 : * \param[in] then_flags Action flags for \p then for ordering purposes
235 : * \param[in,out] order Action wrapper for \p first in ordering
236 : * \param[in,out] scheduler Scheduler data
237 : *
238 : * \return Group of enum pcmk__updated flags
239 : */
240 : static uint32_t
241 0 : update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then,
242 : uint32_t first_flags, uint32_t then_flags,
243 : pcmk__related_action_t *order,
244 : pcmk_scheduler_t *scheduler)
245 : {
246 0 : uint32_t changed = pcmk__updated_none;
247 :
248 : /* The node will only be used for clones. If interleaved, node will be NULL,
249 : * otherwise the ordering scope will be limited to the node. Normally, the
250 : * whole 'then' clone should restart if 'first' is restarted, so then->node
251 : * is needed.
252 : */
253 0 : pcmk_node_t *node = then->node;
254 :
255 0 : if (pcmk_is_set(order->type, pcmk__ar_first_implies_same_node_then)) {
256 : /* For unfencing, only instances of 'then' on the same node as 'first'
257 : * (the unfencing operation) should restart, so reset node to
258 : * first->node, at which point this case is handled like a normal
259 : * pcmk__ar_first_implies_then.
260 : */
261 0 : pcmk__clear_relation_flags(order->type,
262 : pcmk__ar_first_implies_same_node_then);
263 0 : pcmk__set_relation_flags(order->type, pcmk__ar_first_implies_then);
264 0 : node = first->node;
265 0 : pcmk__rsc_trace(then->rsc,
266 : "%s then %s: mapped "
267 : "pcmk__ar_first_implies_same_node_then to "
268 : "pcmk__ar_first_implies_then on %s",
269 : first->uuid, then->uuid, pcmk__node_name(node));
270 : }
271 :
272 0 : if (pcmk_is_set(order->type, pcmk__ar_first_implies_then)) {
273 0 : if (then->rsc != NULL) {
274 0 : changed |= update(then->rsc, first, then, node,
275 : first_flags & pcmk_action_optional,
276 : pcmk_action_optional, pcmk__ar_first_implies_then,
277 : scheduler);
278 0 : } else if (!pcmk_is_set(first_flags, pcmk_action_optional)
279 0 : && pcmk_is_set(then->flags, pcmk_action_optional)) {
280 0 : pcmk__clear_action_flags(then, pcmk_action_optional);
281 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_then);
282 : }
283 0 : pcmk__rsc_trace(then->rsc,
284 : "%s then %s: %s after pcmk__ar_first_implies_then",
285 : first->uuid, then->uuid,
286 : (changed? "changed" : "unchanged"));
287 : }
288 :
289 0 : if (pcmk_is_set(order->type, pcmk__ar_intermediate_stop)
290 0 : && (then->rsc != NULL)) {
291 0 : enum pe_action_flags restart = pcmk_action_optional
292 : |pcmk_action_runnable;
293 :
294 0 : changed |= update(then->rsc, first, then, node, first_flags, restart,
295 : pcmk__ar_intermediate_stop, scheduler);
296 0 : pcmk__rsc_trace(then->rsc,
297 : "%s then %s: %s after pcmk__ar_intermediate_stop",
298 : first->uuid, then->uuid,
299 : (changed? "changed" : "unchanged"));
300 : }
301 :
302 0 : if (pcmk_is_set(order->type, pcmk__ar_then_implies_first)) {
303 0 : if (first->rsc != NULL) {
304 0 : changed |= update(first->rsc, first, then, node, first_flags,
305 : pcmk_action_optional, pcmk__ar_then_implies_first,
306 : scheduler);
307 0 : } else if (!pcmk_is_set(first_flags, pcmk_action_optional)
308 0 : && pcmk_is_set(first->flags, pcmk_action_runnable)) {
309 0 : pcmk__clear_action_flags(first, pcmk_action_runnable);
310 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_first);
311 : }
312 0 : pcmk__rsc_trace(then->rsc,
313 : "%s then %s: %s after pcmk__ar_then_implies_first",
314 : first->uuid, then->uuid,
315 : (changed? "changed" : "unchanged"));
316 : }
317 :
318 0 : if (pcmk_is_set(order->type, pcmk__ar_promoted_then_implies_first)) {
319 0 : if (then->rsc != NULL) {
320 0 : changed |= update(then->rsc, first, then, node,
321 : first_flags & pcmk_action_optional,
322 : pcmk_action_optional,
323 : pcmk__ar_promoted_then_implies_first, scheduler);
324 : }
325 0 : pcmk__rsc_trace(then->rsc,
326 : "%s then %s: %s after "
327 : "pcmk__ar_promoted_then_implies_first",
328 : first->uuid, then->uuid,
329 : (changed? "changed" : "unchanged"));
330 : }
331 :
332 0 : if (pcmk_is_set(order->type, pcmk__ar_min_runnable)) {
333 0 : if (then->rsc != NULL) {
334 0 : changed |= update(then->rsc, first, then, node, first_flags,
335 : pcmk_action_runnable, pcmk__ar_min_runnable,
336 : scheduler);
337 :
338 0 : } else if (pcmk_is_set(first_flags, pcmk_action_runnable)) {
339 : // We have another runnable instance of "first"
340 0 : then->runnable_before++;
341 :
342 : /* Mark "then" as runnable if it requires a certain number of
343 : * "before" instances to be runnable, and they now are.
344 : */
345 0 : if ((then->runnable_before >= then->required_runnable_before)
346 0 : && !pcmk_is_set(then->flags, pcmk_action_runnable)) {
347 :
348 0 : pcmk__set_action_flags(then, pcmk_action_runnable);
349 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_then);
350 : }
351 : }
352 0 : pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_min_runnable",
353 : first->uuid, then->uuid,
354 : (changed? "changed" : "unchanged"));
355 : }
356 :
357 0 : if (pcmk_is_set(order->type, pcmk__ar_nested_remote_probe)
358 0 : && (then->rsc != NULL)) {
359 :
360 0 : if (!pcmk_is_set(first_flags, pcmk_action_runnable)
361 0 : && (first->rsc != NULL) && (first->rsc->running_on != NULL)) {
362 :
363 0 : pcmk__rsc_trace(then->rsc,
364 : "%s then %s: ignoring because first is stopping",
365 : first->uuid, then->uuid);
366 0 : order->type = (enum pe_ordering) pcmk__ar_none;
367 : } else {
368 0 : changed |= update(then->rsc, first, then, node, first_flags,
369 : pcmk_action_runnable,
370 : pcmk__ar_unrunnable_first_blocks, scheduler);
371 : }
372 0 : pcmk__rsc_trace(then->rsc,
373 : "%s then %s: %s after pcmk__ar_nested_remote_probe",
374 : first->uuid, then->uuid,
375 : (changed? "changed" : "unchanged"));
376 : }
377 :
378 0 : if (pcmk_is_set(order->type, pcmk__ar_unrunnable_first_blocks)) {
379 0 : if (then->rsc != NULL) {
380 0 : changed |= update(then->rsc, first, then, node, first_flags,
381 : pcmk_action_runnable,
382 : pcmk__ar_unrunnable_first_blocks, scheduler);
383 :
384 0 : } else if (!pcmk_is_set(first_flags, pcmk_action_runnable)
385 0 : && pcmk_is_set(then->flags, pcmk_action_runnable)) {
386 :
387 0 : pcmk__clear_action_flags(then, pcmk_action_runnable);
388 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_then);
389 : }
390 0 : pcmk__rsc_trace(then->rsc,
391 : "%s then %s: %s after pcmk__ar_unrunnable_first_blocks",
392 : first->uuid, then->uuid,
393 : (changed? "changed" : "unchanged"));
394 : }
395 :
396 0 : if (pcmk_is_set(order->type, pcmk__ar_unmigratable_then_blocks)) {
397 0 : if (then->rsc != NULL) {
398 0 : changed |= update(then->rsc, first, then, node, first_flags,
399 : pcmk_action_optional,
400 : pcmk__ar_unmigratable_then_blocks, scheduler);
401 : }
402 0 : pcmk__rsc_trace(then->rsc,
403 : "%s then %s: %s after "
404 : "pcmk__ar_unmigratable_then_blocks",
405 : first->uuid, then->uuid,
406 : (changed? "changed" : "unchanged"));
407 : }
408 :
409 0 : if (pcmk_is_set(order->type, pcmk__ar_first_else_then)) {
410 0 : if (then->rsc != NULL) {
411 0 : changed |= update(then->rsc, first, then, node, first_flags,
412 : pcmk_action_optional, pcmk__ar_first_else_then,
413 : scheduler);
414 : }
415 0 : pcmk__rsc_trace(then->rsc,
416 : "%s then %s: %s after pcmk__ar_first_else_then",
417 : first->uuid, then->uuid,
418 : (changed? "changed" : "unchanged"));
419 : }
420 :
421 0 : if (pcmk_is_set(order->type, pcmk__ar_ordered)) {
422 0 : if (then->rsc != NULL) {
423 0 : changed |= update(then->rsc, first, then, node, first_flags,
424 : pcmk_action_runnable, pcmk__ar_ordered,
425 : scheduler);
426 : }
427 0 : pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_ordered",
428 : first->uuid, then->uuid,
429 : (changed? "changed" : "unchanged"));
430 : }
431 :
432 0 : if (pcmk_is_set(order->type, pcmk__ar_asymmetric)) {
433 0 : if (then->rsc != NULL) {
434 0 : changed |= update(then->rsc, first, then, node, first_flags,
435 : pcmk_action_runnable, pcmk__ar_asymmetric,
436 : scheduler);
437 : }
438 0 : pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_asymmetric",
439 : first->uuid, then->uuid,
440 : (changed? "changed" : "unchanged"));
441 : }
442 :
443 0 : if (pcmk_is_set(first->flags, pcmk_action_runnable)
444 0 : && pcmk_is_set(order->type, pcmk__ar_first_implies_then_graphed)
445 0 : && !pcmk_is_set(first_flags, pcmk_action_optional)) {
446 :
447 0 : pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required",
448 : then->uuid, first->uuid);
449 0 : pcmk__set_action_flags(then, pcmk_action_always_in_graph);
450 : // Don't bother marking 'then' as changed just for this
451 : }
452 :
453 0 : if (pcmk_is_set(order->type, pcmk__ar_then_implies_first_graphed)
454 0 : && !pcmk_is_set(then_flags, pcmk_action_optional)) {
455 :
456 0 : pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required",
457 : first->uuid, then->uuid);
458 0 : pcmk__set_action_flags(first, pcmk_action_always_in_graph);
459 : // Don't bother marking 'first' as changed just for this
460 : }
461 :
462 0 : if (pcmk_any_flags_set(order->type, pcmk__ar_first_implies_then
463 : |pcmk__ar_then_implies_first
464 : |pcmk__ar_intermediate_stop)
465 0 : && (first->rsc != NULL)
466 0 : && !pcmk_is_set(first->rsc->flags, pcmk_rsc_managed)
467 0 : && pcmk_is_set(first->rsc->flags, pcmk_rsc_blocked)
468 0 : && !pcmk_is_set(first->flags, pcmk_action_runnable)
469 0 : && pcmk__str_eq(first->task, PCMK_ACTION_STOP, pcmk__str_none)) {
470 :
471 0 : if (pcmk_is_set(then->flags, pcmk_action_runnable)) {
472 0 : pcmk__clear_action_flags(then, pcmk_action_runnable);
473 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_then);
474 : }
475 0 : pcmk__rsc_trace(then->rsc,
476 : "%s then %s: %s after checking whether first "
477 : "is blocked, unmanaged, unrunnable stop",
478 : first->uuid, then->uuid,
479 : (changed? "changed" : "unchanged"));
480 : }
481 :
482 0 : return changed;
483 : }
484 :
485 : // Convenience macros for logging action properties
486 :
487 : #define action_type_str(flags) \
488 : (pcmk_is_set((flags), pcmk_action_pseudo)? "pseudo-action" : "action")
489 :
490 : #define action_optional_str(flags) \
491 : (pcmk_is_set((flags), pcmk_action_optional)? "optional" : "required")
492 :
493 : #define action_runnable_str(flags) \
494 : (pcmk_is_set((flags), pcmk_action_runnable)? "runnable" : "unrunnable")
495 :
496 : #define action_node_str(a) \
497 : (((a)->node == NULL)? "no node" : (a)->node->details->uname)
498 :
499 : /*!
500 : * \internal
501 : * \brief Update an action's flags for all orderings where it is "then"
502 : *
503 : * \param[in,out] then Action to update
504 : * \param[in,out] scheduler Scheduler data
505 : */
506 : void
507 0 : pcmk__update_action_for_orderings(pcmk_action_t *then,
508 : pcmk_scheduler_t *scheduler)
509 : {
510 0 : GList *lpc = NULL;
511 0 : uint32_t changed = pcmk__updated_none;
512 0 : int last_flags = then->flags;
513 :
514 0 : pcmk__rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
515 : action_type_str(then->flags), then->uuid,
516 : action_optional_str(then->flags),
517 : action_runnable_str(then->flags), action_node_str(then));
518 :
519 0 : if (pcmk_is_set(then->flags, pcmk_action_min_runnable)) {
520 : /* Initialize current known "runnable before" actions. As
521 : * update_action_for_ordering_flags() is called for each of then's
522 : * before actions, this number will increment as runnable 'first'
523 : * actions are encountered.
524 : */
525 0 : then->runnable_before = 0;
526 :
527 0 : if (then->required_runnable_before == 0) {
528 : /* @COMPAT This ordering constraint uses the deprecated
529 : * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE attribute. Treat it like
530 : * PCMK_META_CLONE_MIN=1.
531 : */
532 0 : then->required_runnable_before = 1;
533 : }
534 :
535 : /* The pcmk__ar_min_runnable clause of
536 : * update_action_for_ordering_flags() (called below)
537 : * will reset runnable if appropriate.
538 : */
539 0 : pcmk__clear_action_flags(then, pcmk_action_runnable);
540 : }
541 :
542 0 : for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
543 0 : pcmk__related_action_t *other = lpc->data;
544 0 : pcmk_action_t *first = other->action;
545 :
546 0 : pcmk_node_t *then_node = then->node;
547 0 : pcmk_node_t *first_node = first->node;
548 :
549 0 : if ((first->rsc != NULL)
550 0 : && (first->rsc->variant == pcmk_rsc_variant_group)
551 0 : && pcmk__str_eq(first->task, PCMK_ACTION_START, pcmk__str_none)) {
552 :
553 0 : first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
554 0 : if (first_node != NULL) {
555 0 : pcmk__rsc_trace(first->rsc, "Found %s for 'first' %s",
556 : pcmk__node_name(first_node), first->uuid);
557 : }
558 : }
559 :
560 0 : if ((then->rsc != NULL)
561 0 : && (then->rsc->variant == pcmk_rsc_variant_group)
562 0 : && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)) {
563 :
564 0 : then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
565 0 : if (then_node != NULL) {
566 0 : pcmk__rsc_trace(then->rsc, "Found %s for 'then' %s",
567 : pcmk__node_name(then_node), then->uuid);
568 : }
569 : }
570 :
571 : // Disable constraint if it only applies when on same node, but isn't
572 0 : if (pcmk_is_set(other->type, pcmk__ar_if_on_same_node)
573 0 : && (first_node != NULL) && (then_node != NULL)
574 0 : && !pcmk__same_node(first_node, then_node)) {
575 :
576 0 : pcmk__rsc_trace(then->rsc,
577 : "Disabled ordering %s on %s then %s on %s: "
578 : "not same node",
579 : other->action->uuid, pcmk__node_name(first_node),
580 : then->uuid, pcmk__node_name(then_node));
581 0 : other->type = (enum pe_ordering) pcmk__ar_none;
582 0 : continue;
583 : }
584 :
585 0 : pcmk__clear_updated_flags(changed, then, pcmk__updated_first);
586 :
587 0 : if ((first->rsc != NULL)
588 0 : && pcmk_is_set(other->type, pcmk__ar_then_cancels_first)
589 0 : && !pcmk_is_set(then->flags, pcmk_action_optional)) {
590 :
591 : /* 'then' is required, so we must abandon 'first'
592 : * (e.g. a required stop cancels any agent reload).
593 : */
594 0 : pcmk__set_action_flags(other->action, pcmk_action_optional);
595 0 : if (!strcmp(first->task, PCMK_ACTION_RELOAD_AGENT)) {
596 0 : pcmk__clear_rsc_flags(first->rsc, pcmk_rsc_reload);
597 : }
598 : }
599 :
600 0 : if ((first->rsc != NULL) && (then->rsc != NULL)
601 0 : && (first->rsc != then->rsc) && !is_parent(then->rsc, first->rsc)) {
602 0 : first = action_for_ordering(first);
603 : }
604 0 : if (first != other->action) {
605 0 : pcmk__rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
606 : then->uuid, first->uuid, other->action->uuid);
607 : }
608 :
609 0 : pcmk__rsc_trace(then->rsc,
610 : "%s (%#.6x) then %s (%#.6x): type=%#.6x node=%s",
611 : first->uuid, first->flags, then->uuid, then->flags,
612 : other->type, action_node_str(first));
613 :
614 0 : if (first == other->action) {
615 : /* 'first' was not remapped (e.g. from 'start' to 'running'), which
616 : * could mean it is a non-resource action, a primitive resource
617 : * action, or already expanded.
618 : */
619 : uint32_t first_flags, then_flags;
620 :
621 0 : first_flags = action_flags_for_ordering(first, then_node);
622 0 : then_flags = action_flags_for_ordering(then, first_node);
623 :
624 0 : changed |= update_action_for_ordering_flags(first, then,
625 : first_flags, then_flags,
626 : other, scheduler);
627 :
628 : /* 'first' was for a complex resource (clone, group, etc),
629 : * create a new dependency if necessary
630 : */
631 0 : } else if (order_actions(first, then, other->type)) {
632 : /* This was the first time 'first' and 'then' were associated,
633 : * start again to get the new actions_before list
634 : */
635 0 : pcmk__set_updated_flags(changed, then, pcmk__updated_then);
636 0 : pcmk__rsc_trace(then->rsc,
637 : "Disabled ordering %s then %s in favor of %s "
638 : "then %s",
639 : other->action->uuid, then->uuid, first->uuid,
640 : then->uuid);
641 0 : other->type = (enum pe_ordering) pcmk__ar_none;
642 : }
643 :
644 :
645 0 : if (pcmk_is_set(changed, pcmk__updated_first)) {
646 0 : crm_trace("Re-processing %s and its 'after' actions "
647 : "because it changed", first->uuid);
648 0 : for (GList *lpc2 = first->actions_after; lpc2 != NULL;
649 0 : lpc2 = lpc2->next) {
650 0 : pcmk__related_action_t *other = lpc2->data;
651 :
652 0 : pcmk__update_action_for_orderings(other->action, scheduler);
653 : }
654 0 : pcmk__update_action_for_orderings(first, scheduler);
655 : }
656 : }
657 :
658 0 : if (pcmk_is_set(then->flags, pcmk_action_min_runnable)) {
659 0 : if (last_flags == then->flags) {
660 0 : pcmk__clear_updated_flags(changed, then, pcmk__updated_then);
661 : } else {
662 0 : pcmk__set_updated_flags(changed, then, pcmk__updated_then);
663 : }
664 : }
665 :
666 0 : if (pcmk_is_set(changed, pcmk__updated_then)) {
667 0 : crm_trace("Re-processing %s and its 'after' actions because it changed",
668 : then->uuid);
669 0 : if (pcmk_is_set(last_flags, pcmk_action_runnable)
670 0 : && !pcmk_is_set(then->flags, pcmk_action_runnable)) {
671 0 : pcmk__block_colocation_dependents(then);
672 : }
673 0 : pcmk__update_action_for_orderings(then, scheduler);
674 0 : for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
675 0 : pcmk__related_action_t *other = lpc->data;
676 :
677 0 : pcmk__update_action_for_orderings(other->action, scheduler);
678 : }
679 : }
680 0 : }
681 :
682 : static inline bool
683 0 : is_primitive_action(const pcmk_action_t *action)
684 : {
685 0 : return (action != NULL) && (action->rsc != NULL)
686 0 : && (action->rsc->variant == pcmk_rsc_variant_primitive);
687 : }
688 :
689 : /*!
690 : * \internal
691 : * \brief Clear a single action flag and set reason text
692 : *
693 : * \param[in,out] action Action whose flag should be cleared
694 : * \param[in] flag Action flag that should be cleared
695 : * \param[in] reason Action that is the reason why flag is being cleared
696 : */
697 : #define clear_action_flag_because(action, flag, reason) do { \
698 : if (pcmk_is_set((action)->flags, (flag))) { \
699 : pcmk__clear_action_flags(action, flag); \
700 : if ((action)->rsc != (reason)->rsc) { \
701 : char *reason_text = pe__action2reason((reason), (flag)); \
702 : pe_action_set_reason((action), reason_text, false); \
703 : free(reason_text); \
704 : } \
705 : } \
706 : } while (0)
707 :
708 : /*!
709 : * \internal
710 : * \brief Update actions in an asymmetric ordering
711 : *
712 : * If the "first" action in an asymmetric ordering is unrunnable, make the
713 : * "second" action unrunnable as well, if appropriate.
714 : *
715 : * \param[in] first 'First' action in an asymmetric ordering
716 : * \param[in,out] then 'Then' action in an asymmetric ordering
717 : */
718 : static void
719 0 : handle_asymmetric_ordering(const pcmk_action_t *first, pcmk_action_t *then)
720 : {
721 : /* Only resource actions after an unrunnable 'first' action need updates for
722 : * asymmetric ordering.
723 : */
724 0 : if ((then->rsc == NULL)
725 0 : || pcmk_is_set(first->flags, pcmk_action_runnable)) {
726 0 : return;
727 : }
728 :
729 : // Certain optional 'then' actions are unaffected by unrunnable 'first'
730 0 : if (pcmk_is_set(then->flags, pcmk_action_optional)) {
731 0 : enum rsc_role_e then_rsc_role = then->rsc->fns->state(then->rsc, TRUE);
732 :
733 0 : if ((then_rsc_role == pcmk_role_stopped)
734 0 : && pcmk__str_eq(then->task, PCMK_ACTION_STOP, pcmk__str_none)) {
735 : /* If 'then' should stop after 'first' but is already stopped, the
736 : * ordering is irrelevant.
737 : */
738 0 : return;
739 0 : } else if ((then_rsc_role >= pcmk_role_started)
740 0 : && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)
741 0 : && pe__rsc_running_on_only(then->rsc, then->node)) {
742 : /* Similarly if 'then' should start after 'first' but is already
743 : * started on a single node.
744 : */
745 0 : return;
746 : }
747 : }
748 :
749 : // 'First' can't run, so 'then' can't either
750 0 : clear_action_flag_because(then, pcmk_action_optional, first);
751 0 : clear_action_flag_because(then, pcmk_action_runnable, first);
752 : }
753 :
754 : /*!
755 : * \internal
756 : * \brief Set action bits appropriately when pe_restart_order is used
757 : *
758 : * \param[in,out] first 'First' action in an ordering with pe_restart_order
759 : * \param[in,out] then 'Then' action in an ordering with pe_restart_order
760 : * \param[in] filter What action flags to care about
761 : *
762 : * \note pe_restart_order is set for "stop resource before starting it" and
763 : * "stop later group member before stopping earlier group member"
764 : */
765 : static void
766 0 : handle_restart_ordering(pcmk_action_t *first, pcmk_action_t *then,
767 : uint32_t filter)
768 : {
769 0 : const char *reason = NULL;
770 :
771 0 : CRM_ASSERT(is_primitive_action(first));
772 0 : CRM_ASSERT(is_primitive_action(then));
773 :
774 : // We need to update the action in two cases:
775 :
776 : // ... if 'then' is required
777 0 : if (pcmk_is_set(filter, pcmk_action_optional)
778 0 : && !pcmk_is_set(then->flags, pcmk_action_optional)) {
779 0 : reason = "restart";
780 : }
781 :
782 : /* ... if 'then' is unrunnable action on same resource (if a resource
783 : * should restart but can't start, we still want to stop)
784 : */
785 0 : if (pcmk_is_set(filter, pcmk_action_runnable)
786 0 : && !pcmk_is_set(then->flags, pcmk_action_runnable)
787 0 : && pcmk_is_set(then->rsc->flags, pcmk_rsc_managed)
788 0 : && (first->rsc == then->rsc)) {
789 0 : reason = "stop";
790 : }
791 :
792 0 : if (reason == NULL) {
793 0 : return;
794 : }
795 :
796 0 : pcmk__rsc_trace(first->rsc, "Handling %s -> %s for %s",
797 : first->uuid, then->uuid, reason);
798 :
799 : // Make 'first' required if it is runnable
800 0 : if (pcmk_is_set(first->flags, pcmk_action_runnable)) {
801 0 : clear_action_flag_because(first, pcmk_action_optional, then);
802 : }
803 :
804 : // Make 'first' required if 'then' is required
805 0 : if (!pcmk_is_set(then->flags, pcmk_action_optional)) {
806 0 : clear_action_flag_because(first, pcmk_action_optional, then);
807 : }
808 :
809 : // Make 'first' unmigratable if 'then' is unmigratable
810 0 : if (!pcmk_is_set(then->flags, pcmk_action_migratable)) {
811 0 : clear_action_flag_because(first, pcmk_action_migratable, then);
812 : }
813 :
814 : // Make 'then' unrunnable if 'first' is required but unrunnable
815 0 : if (!pcmk_is_set(first->flags, pcmk_action_optional)
816 0 : && !pcmk_is_set(first->flags, pcmk_action_runnable)) {
817 0 : clear_action_flag_because(then, pcmk_action_runnable, first);
818 : }
819 : }
820 :
821 : /*!
822 : * \internal
823 : * \brief Update two actions according to an ordering between them
824 : *
825 : * Given information about an ordering of two actions, update the actions' flags
826 : * (and runnable_before members if appropriate) as appropriate for the ordering.
827 : * Effects may cascade to other orderings involving the actions as well.
828 : *
829 : * \param[in,out] first 'First' action in an ordering
830 : * \param[in,out] then 'Then' action in an ordering
831 : * \param[in] node If not NULL, limit scope of ordering to this node
832 : * (ignored)
833 : * \param[in] flags Action flags for \p first for ordering purposes
834 : * \param[in] filter Action flags to limit scope of certain updates (may
835 : * include pcmk_action_optional to affect only
836 : * mandatory actions, and pcmk_action_runnable to
837 : * affect only runnable actions)
838 : * \param[in] type Group of enum pcmk__action_relation_flags to apply
839 : * \param[in,out] scheduler Scheduler data
840 : *
841 : * \return Group of enum pcmk__updated flags indicating what was updated
842 : */
843 : uint32_t
844 0 : pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then,
845 : const pcmk_node_t *node, uint32_t flags,
846 : uint32_t filter, uint32_t type,
847 : pcmk_scheduler_t *scheduler)
848 : {
849 0 : uint32_t changed = pcmk__updated_none;
850 0 : uint32_t then_flags = 0U;
851 0 : uint32_t first_flags = 0U;
852 :
853 0 : CRM_ASSERT((first != NULL) && (then != NULL) && (scheduler != NULL));
854 :
855 0 : then_flags = then->flags;
856 0 : first_flags = first->flags;
857 0 : if (pcmk_is_set(type, pcmk__ar_asymmetric)) {
858 0 : handle_asymmetric_ordering(first, then);
859 : }
860 :
861 0 : if (pcmk_is_set(type, pcmk__ar_then_implies_first)
862 0 : && !pcmk_is_set(then_flags, pcmk_action_optional)) {
863 : // Then is required, and implies first should be, too
864 :
865 0 : if (pcmk_is_set(filter, pcmk_action_optional)
866 0 : && !pcmk_is_set(flags, pcmk_action_optional)
867 0 : && pcmk_is_set(first_flags, pcmk_action_optional)) {
868 0 : clear_action_flag_because(first, pcmk_action_optional, then);
869 : }
870 :
871 0 : if (pcmk_is_set(flags, pcmk_action_migratable)
872 0 : && !pcmk_is_set(then->flags, pcmk_action_migratable)) {
873 0 : clear_action_flag_because(first, pcmk_action_migratable, then);
874 : }
875 : }
876 :
877 0 : if (pcmk_is_set(type, pcmk__ar_promoted_then_implies_first)
878 0 : && (then->rsc != NULL) && (then->rsc->role == pcmk_role_promoted)
879 0 : && pcmk_is_set(filter, pcmk_action_optional)
880 0 : && !pcmk_is_set(then->flags, pcmk_action_optional)) {
881 :
882 0 : clear_action_flag_because(first, pcmk_action_optional, then);
883 :
884 0 : if (pcmk_is_set(first->flags, pcmk_action_migratable)
885 0 : && !pcmk_is_set(then->flags, pcmk_action_migratable)) {
886 0 : clear_action_flag_because(first, pcmk_action_migratable, then);
887 : }
888 : }
889 :
890 0 : if (pcmk_is_set(type, pcmk__ar_unmigratable_then_blocks)
891 0 : && pcmk_is_set(filter, pcmk_action_optional)) {
892 :
893 0 : if (!pcmk_all_flags_set(then->flags, pcmk_action_migratable
894 : |pcmk_action_runnable)) {
895 0 : clear_action_flag_because(first, pcmk_action_runnable, then);
896 : }
897 :
898 0 : if (!pcmk_is_set(then->flags, pcmk_action_optional)) {
899 0 : clear_action_flag_because(first, pcmk_action_optional, then);
900 : }
901 : }
902 :
903 0 : if (pcmk_is_set(type, pcmk__ar_first_else_then)
904 0 : && pcmk_is_set(filter, pcmk_action_optional)
905 0 : && !pcmk_is_set(first->flags, pcmk_action_runnable)) {
906 :
907 0 : clear_action_flag_because(then, pcmk_action_migratable, first);
908 0 : pcmk__clear_action_flags(then, pcmk_action_pseudo);
909 : }
910 :
911 0 : if (pcmk_is_set(type, pcmk__ar_unrunnable_first_blocks)
912 0 : && pcmk_is_set(filter, pcmk_action_runnable)
913 0 : && pcmk_is_set(then->flags, pcmk_action_runnable)
914 0 : && !pcmk_is_set(flags, pcmk_action_runnable)) {
915 :
916 0 : clear_action_flag_because(then, pcmk_action_runnable, first);
917 0 : clear_action_flag_because(then, pcmk_action_migratable, first);
918 : }
919 :
920 0 : if (pcmk_is_set(type, pcmk__ar_first_implies_then)
921 0 : && pcmk_is_set(filter, pcmk_action_optional)
922 0 : && pcmk_is_set(then->flags, pcmk_action_optional)
923 0 : && !pcmk_is_set(flags, pcmk_action_optional)
924 0 : && !pcmk_is_set(first->flags, pcmk_action_migratable)) {
925 :
926 0 : clear_action_flag_because(then, pcmk_action_optional, first);
927 : }
928 :
929 0 : if (pcmk_is_set(type, pcmk__ar_intermediate_stop)) {
930 0 : handle_restart_ordering(first, then, filter);
931 : }
932 :
933 0 : if (then_flags != then->flags) {
934 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_then);
935 0 : pcmk__rsc_trace(then->rsc,
936 : "%s on %s: flags are now %#.6x (was %#.6x) "
937 : "because of 'first' %s (%#.6x)",
938 : then->uuid, pcmk__node_name(then->node),
939 : then->flags, then_flags, first->uuid, first->flags);
940 :
941 0 : if ((then->rsc != NULL) && (then->rsc->parent != NULL)) {
942 : // Required to handle "X_stop then X_start" for cloned groups
943 0 : pcmk__update_action_for_orderings(then, scheduler);
944 : }
945 : }
946 :
947 0 : if (first_flags != first->flags) {
948 0 : pcmk__set_updated_flags(changed, first, pcmk__updated_first);
949 0 : pcmk__rsc_trace(first->rsc,
950 : "%s on %s: flags are now %#.6x (was %#.6x) "
951 : "because of 'then' %s (%#.6x)",
952 : first->uuid, pcmk__node_name(first->node),
953 : first->flags, first_flags, then->uuid, then->flags);
954 : }
955 :
956 0 : return changed;
957 : }
958 :
959 : /*!
960 : * \internal
961 : * \brief Trace-log an action (optionally with its dependent actions)
962 : *
963 : * \param[in] pre_text If not NULL, prefix the log with this plus ": "
964 : * \param[in] action Action to log
965 : * \param[in] details If true, recursively log dependent actions
966 : */
967 : void
968 0 : pcmk__log_action(const char *pre_text, const pcmk_action_t *action,
969 : bool details)
970 : {
971 0 : const char *node_uname = NULL;
972 0 : const char *node_uuid = NULL;
973 0 : const char *desc = NULL;
974 :
975 0 : CRM_CHECK(action != NULL, return);
976 :
977 0 : if (!pcmk_is_set(action->flags, pcmk_action_pseudo)) {
978 0 : if (action->node != NULL) {
979 0 : node_uname = action->node->details->uname;
980 0 : node_uuid = action->node->details->id;
981 : } else {
982 0 : node_uname = "<none>";
983 : }
984 : }
985 :
986 0 : switch (pcmk_parse_action(action->task)) {
987 0 : case pcmk_action_fence:
988 : case pcmk_action_shutdown:
989 0 : if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
990 0 : desc = "Pseudo ";
991 0 : } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
992 0 : desc = "Optional ";
993 0 : } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
994 0 : desc = "!!Non-Startable!! ";
995 : } else {
996 0 : desc = "(Provisional) ";
997 : }
998 0 : crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
999 : ((pre_text == NULL)? "" : pre_text),
1000 : ((pre_text == NULL)? "" : ": "),
1001 : desc, action->id, action->uuid,
1002 : (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1003 : (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1004 : (node_uuid? ")" : ""));
1005 0 : break;
1006 0 : default:
1007 0 : if (pcmk_is_set(action->flags, pcmk_action_optional)) {
1008 0 : desc = "Optional ";
1009 0 : } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
1010 0 : desc = "Pseudo ";
1011 0 : } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
1012 0 : desc = "!!Non-Startable!! ";
1013 : } else {
1014 0 : desc = "(Provisional) ";
1015 : }
1016 0 : crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
1017 : ((pre_text == NULL)? "" : pre_text),
1018 : ((pre_text == NULL)? "" : ": "),
1019 : desc, action->id, action->uuid,
1020 : (action->rsc? action->rsc->id : "<none>"),
1021 : (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1022 : (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1023 : (node_uuid? ")" : ""));
1024 0 : break;
1025 : }
1026 :
1027 0 : if (details) {
1028 0 : const GList *iter = NULL;
1029 0 : const pcmk__related_action_t *other = NULL;
1030 :
1031 0 : crm_trace("\t\t====== Preceding Actions");
1032 0 : for (iter = action->actions_before; iter != NULL; iter = iter->next) {
1033 0 : other = (const pcmk__related_action_t *) iter->data;
1034 0 : pcmk__log_action("\t\t", other->action, false);
1035 : }
1036 0 : crm_trace("\t\t====== Subsequent Actions");
1037 0 : for (iter = action->actions_after; iter != NULL; iter = iter->next) {
1038 0 : other = (const pcmk__related_action_t *) iter->data;
1039 0 : pcmk__log_action("\t\t", other->action, false);
1040 : }
1041 0 : crm_trace("\t\t====== End");
1042 :
1043 : } else {
1044 0 : crm_trace("\t\t(before=%d, after=%d)",
1045 : g_list_length(action->actions_before),
1046 : g_list_length(action->actions_after));
1047 : }
1048 : }
1049 :
1050 : /*!
1051 : * \internal
1052 : * \brief Create a new shutdown action for a node
1053 : *
1054 : * \param[in,out] node Node being shut down
1055 : *
1056 : * \return Newly created shutdown action for \p node
1057 : */
1058 : pcmk_action_t *
1059 0 : pcmk__new_shutdown_action(pcmk_node_t *node)
1060 : {
1061 0 : char *shutdown_id = NULL;
1062 0 : pcmk_action_t *shutdown_op = NULL;
1063 :
1064 0 : CRM_ASSERT(node != NULL);
1065 :
1066 0 : shutdown_id = crm_strdup_printf("%s-%s", PCMK_ACTION_DO_SHUTDOWN,
1067 0 : node->details->uname);
1068 :
1069 0 : shutdown_op = custom_action(NULL, shutdown_id, PCMK_ACTION_DO_SHUTDOWN,
1070 0 : node, FALSE, node->details->data_set);
1071 :
1072 0 : pcmk__order_stops_before_shutdown(node, shutdown_op);
1073 0 : pcmk__insert_meta(shutdown_op, PCMK__META_OP_NO_WAIT, PCMK_VALUE_TRUE);
1074 0 : return shutdown_op;
1075 : }
1076 :
1077 : /*!
1078 : * \internal
1079 : * \brief Calculate and add an operation digest to XML
1080 : *
1081 : * Calculate an operation digest, which enables us to later determine when a
1082 : * restart is needed due to the resource's parameters being changed, and add it
1083 : * to given XML.
1084 : *
1085 : * \param[in] op Operation result from executor
1086 : * \param[in,out] update XML to add digest to
1087 : */
1088 : static void
1089 0 : add_op_digest_to_xml(const lrmd_event_data_t *op, xmlNode *update)
1090 : {
1091 0 : char *digest = NULL;
1092 0 : xmlNode *args_xml = NULL;
1093 :
1094 0 : if (op->params == NULL) {
1095 0 : return;
1096 : }
1097 0 : args_xml = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
1098 0 : g_hash_table_foreach(op->params, hash2field, args_xml);
1099 0 : pcmk__filter_op_for_digest(args_xml);
1100 0 : digest = calculate_operation_digest(args_xml, NULL);
1101 0 : crm_xml_add(update, PCMK__XA_OP_DIGEST, digest);
1102 0 : free_xml(args_xml);
1103 0 : free(digest);
1104 : }
1105 :
1106 : #define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
1107 :
1108 : /*!
1109 : * \internal
1110 : * \brief Create XML for resource operation history update
1111 : *
1112 : * \param[in,out] parent Parent XML node to add to
1113 : * \param[in,out] op Operation event data
1114 : * \param[in] caller_version DC feature set
1115 : * \param[in] target_rc Expected result of operation
1116 : * \param[in] node Name of node on which operation was performed
1117 : * \param[in] origin Arbitrary description of update source
1118 : *
1119 : * \return Newly created XML node for history update
1120 : */
1121 : xmlNode *
1122 0 : pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op,
1123 : const char *caller_version, int target_rc,
1124 : const char *node, const char *origin)
1125 : {
1126 0 : char *key = NULL;
1127 0 : char *magic = NULL;
1128 0 : char *op_id = NULL;
1129 0 : char *op_id_additional = NULL;
1130 0 : char *local_user_data = NULL;
1131 0 : const char *exit_reason = NULL;
1132 :
1133 0 : xmlNode *xml_op = NULL;
1134 0 : const char *task = NULL;
1135 :
1136 0 : CRM_CHECK(op != NULL, return NULL);
1137 0 : crm_trace("Creating history XML for %s-interval %s action for %s on %s "
1138 : "(DC version: %s, origin: %s)",
1139 : pcmk__readable_interval(op->interval_ms), op->op_type, op->rsc_id,
1140 : ((node == NULL)? "no node" : node), caller_version, origin);
1141 :
1142 0 : task = op->op_type;
1143 :
1144 : /* Record a successful agent reload as a start, and a failed one as a
1145 : * monitor, to make life easier for the scheduler when determining the
1146 : * current state.
1147 : *
1148 : * @COMPAT We should check "reload" here only if the operation was for a
1149 : * pre-OCF-1.1 resource agent, but we don't know that here, and we should
1150 : * only ever get results for actions scheduled by us, so we can reasonably
1151 : * assume any "reload" is actually a pre-1.1 agent reload.
1152 : */
1153 0 : if (pcmk__str_any_of(task, PCMK_ACTION_RELOAD, PCMK_ACTION_RELOAD_AGENT,
1154 : NULL)) {
1155 0 : if (op->op_status == PCMK_EXEC_DONE) {
1156 0 : task = PCMK_ACTION_START;
1157 : } else {
1158 0 : task = PCMK_ACTION_MONITOR;
1159 : }
1160 : }
1161 :
1162 0 : key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
1163 0 : if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
1164 0 : const char *n_type = crm_meta_value(op->params, "notify_type");
1165 0 : const char *n_task = crm_meta_value(op->params, "notify_operation");
1166 :
1167 0 : CRM_LOG_ASSERT(n_type != NULL);
1168 0 : CRM_LOG_ASSERT(n_task != NULL);
1169 0 : op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
1170 :
1171 0 : if (op->op_status != PCMK_EXEC_PENDING) {
1172 : /* Ignore notify errors.
1173 : *
1174 : * @TODO It might be better to keep the correct result here, and
1175 : * ignore it in process_graph_event().
1176 : */
1177 0 : lrmd__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
1178 : }
1179 :
1180 : /* Migration history is preserved separately, which usually matters for
1181 : * multiple nodes and is important for future cluster transitions.
1182 : */
1183 0 : } else if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO,
1184 : PCMK_ACTION_MIGRATE_FROM, NULL)) {
1185 0 : op_id = strdup(key);
1186 :
1187 0 : } else if (did_rsc_op_fail(op, target_rc)) {
1188 0 : op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
1189 0 : if (op->interval_ms == 0) {
1190 : /* Ensure 'last' gets updated, in case PCMK_META_RECORD_PENDING is
1191 : * true
1192 : */
1193 0 : op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
1194 : }
1195 0 : exit_reason = op->exit_reason;
1196 :
1197 0 : } else if (op->interval_ms > 0) {
1198 0 : op_id = strdup(key);
1199 :
1200 : } else {
1201 0 : op_id = pcmk__op_key(op->rsc_id, "last", 0);
1202 : }
1203 :
1204 0 : again:
1205 0 : xml_op = pcmk__xe_first_child(parent, PCMK__XE_LRM_RSC_OP, PCMK_XA_ID,
1206 : op_id);
1207 0 : if (xml_op == NULL) {
1208 0 : xml_op = pcmk__xe_create(parent, PCMK__XE_LRM_RSC_OP);
1209 : }
1210 :
1211 0 : if (op->user_data == NULL) {
1212 0 : crm_debug("Generating fake transition key for: " PCMK__OP_FMT
1213 : " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
1214 : op->call_id, origin);
1215 0 : local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
1216 : FAKE_TE_ID);
1217 0 : op->user_data = local_user_data;
1218 : }
1219 :
1220 0 : if (magic == NULL) {
1221 0 : magic = crm_strdup_printf("%d:%d;%s", op->op_status, op->rc,
1222 : (const char *) op->user_data);
1223 : }
1224 :
1225 0 : crm_xml_add(xml_op, PCMK_XA_ID, op_id);
1226 0 : crm_xml_add(xml_op, PCMK__XA_OPERATION_KEY, key);
1227 0 : crm_xml_add(xml_op, PCMK_XA_OPERATION, task);
1228 0 : crm_xml_add(xml_op, PCMK_XA_CRM_DEBUG_ORIGIN, origin);
1229 0 : crm_xml_add(xml_op, PCMK_XA_CRM_FEATURE_SET, caller_version);
1230 0 : crm_xml_add(xml_op, PCMK__XA_TRANSITION_KEY, op->user_data);
1231 0 : crm_xml_add(xml_op, PCMK__XA_TRANSITION_MAGIC, magic);
1232 0 : crm_xml_add(xml_op, PCMK_XA_EXIT_REASON, pcmk__s(exit_reason, ""));
1233 0 : crm_xml_add(xml_op, PCMK__META_ON_NODE, node); // For context during triage
1234 :
1235 0 : crm_xml_add_int(xml_op, PCMK__XA_CALL_ID, op->call_id);
1236 0 : crm_xml_add_int(xml_op, PCMK__XA_RC_CODE, op->rc);
1237 0 : crm_xml_add_int(xml_op, PCMK__XA_OP_STATUS, op->op_status);
1238 0 : crm_xml_add_ms(xml_op, PCMK_META_INTERVAL, op->interval_ms);
1239 :
1240 0 : if (compare_version("2.1", caller_version) <= 0) {
1241 0 : if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
1242 0 : crm_trace("Timing data (" PCMK__OP_FMT
1243 : "): last=%u change=%u exec=%u queue=%u",
1244 : op->rsc_id, op->op_type, op->interval_ms,
1245 : op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
1246 :
1247 0 : if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
1248 : // Recurring ops may have changed rc after initial run
1249 0 : crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE,
1250 0 : (long long) op->t_rcchange);
1251 : } else {
1252 0 : crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE,
1253 0 : (long long) op->t_run);
1254 : }
1255 :
1256 0 : crm_xml_add_int(xml_op, PCMK_XA_EXEC_TIME, op->exec_time);
1257 0 : crm_xml_add_int(xml_op, PCMK_XA_QUEUE_TIME, op->queue_time);
1258 : }
1259 : }
1260 :
1261 0 : if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO,
1262 : PCMK_ACTION_MIGRATE_FROM, NULL)) {
1263 : /* Record PCMK__META_MIGRATE_SOURCE and PCMK__META_MIGRATE_TARGET always
1264 : * for migrate ops.
1265 : */
1266 0 : const char *name = PCMK__META_MIGRATE_SOURCE;
1267 :
1268 0 : crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1269 :
1270 0 : name = PCMK__META_MIGRATE_TARGET;
1271 0 : crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1272 : }
1273 :
1274 0 : add_op_digest_to_xml(op, xml_op);
1275 :
1276 0 : if (op_id_additional) {
1277 0 : free(op_id);
1278 0 : op_id = op_id_additional;
1279 0 : op_id_additional = NULL;
1280 0 : goto again;
1281 : }
1282 :
1283 0 : if (local_user_data) {
1284 0 : free(local_user_data);
1285 0 : op->user_data = NULL;
1286 : }
1287 0 : free(magic);
1288 0 : free(op_id);
1289 0 : free(key);
1290 0 : return xml_op;
1291 : }
1292 :
1293 : /*!
1294 : * \internal
1295 : * \brief Check whether an action shutdown-locks a resource to a node
1296 : *
1297 : * If the PCMK_OPT_SHUTDOWN_LOCK cluster property is set, resources will not be
1298 : * recovered on a different node if cleanly stopped, and may start only on that
1299 : * same node. This function checks whether that applies to a given action, so
1300 : * that the transition graph can be marked appropriately.
1301 : *
1302 : * \param[in] action Action to check
1303 : *
1304 : * \return true if \p action locks its resource to the action's node,
1305 : * otherwise false
1306 : */
1307 : bool
1308 0 : pcmk__action_locks_rsc_to_node(const pcmk_action_t *action)
1309 : {
1310 : // Only resource actions taking place on resource's lock node are locked
1311 0 : if ((action == NULL) || (action->rsc == NULL)
1312 0 : || !pcmk__same_node(action->node, action->rsc->lock_node)) {
1313 0 : return false;
1314 : }
1315 :
1316 : /* During shutdown, only stops are locked (otherwise, another action such as
1317 : * a demote would cause the controller to clear the lock)
1318 : */
1319 0 : if (action->node->details->shutdown && (action->task != NULL)
1320 0 : && (strcmp(action->task, PCMK_ACTION_STOP) != 0)) {
1321 0 : return false;
1322 : }
1323 :
1324 0 : return true;
1325 : }
1326 :
1327 : /* lowest to highest */
1328 : static gint
1329 0 : sort_action_id(gconstpointer a, gconstpointer b)
1330 : {
1331 0 : const pcmk__related_action_t *action_wrapper2 = a;
1332 0 : const pcmk__related_action_t *action_wrapper1 = b;
1333 :
1334 0 : if (a == NULL) {
1335 0 : return 1;
1336 : }
1337 0 : if (b == NULL) {
1338 0 : return -1;
1339 : }
1340 0 : if (action_wrapper1->action->id < action_wrapper2->action->id) {
1341 0 : return 1;
1342 : }
1343 0 : if (action_wrapper1->action->id > action_wrapper2->action->id) {
1344 0 : return -1;
1345 : }
1346 0 : return 0;
1347 : }
1348 :
1349 : /*!
1350 : * \internal
1351 : * \brief Remove any duplicate action inputs, merging action flags
1352 : *
1353 : * \param[in,out] action Action whose inputs should be checked
1354 : */
1355 : void
1356 0 : pcmk__deduplicate_action_inputs(pcmk_action_t *action)
1357 : {
1358 0 : GList *item = NULL;
1359 0 : GList *next = NULL;
1360 0 : pcmk__related_action_t *last_input = NULL;
1361 :
1362 0 : action->actions_before = g_list_sort(action->actions_before,
1363 : sort_action_id);
1364 0 : for (item = action->actions_before; item != NULL; item = next) {
1365 0 : pcmk__related_action_t *input = item->data;
1366 :
1367 0 : next = item->next;
1368 0 : if ((last_input != NULL)
1369 0 : && (input->action->id == last_input->action->id)) {
1370 0 : crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
1371 : input->action->uuid, input->action->id,
1372 : action->uuid, action->id);
1373 :
1374 : /* For the purposes of scheduling, the ordering flags no longer
1375 : * matter, but crm_simulate looks at certain ones when creating a
1376 : * dot graph. Combining the flags is sufficient for that purpose.
1377 : */
1378 0 : last_input->type |= input->type;
1379 0 : if (input->state == pe_link_dumped) {
1380 0 : last_input->state = pe_link_dumped;
1381 : }
1382 :
1383 0 : free(item->data);
1384 0 : action->actions_before = g_list_delete_link(action->actions_before,
1385 : item);
1386 : } else {
1387 0 : last_input = input;
1388 0 : input->state = pe_link_not_dumped;
1389 : }
1390 : }
1391 0 : }
1392 :
1393 : /*!
1394 : * \internal
1395 : * \brief Output all scheduled actions
1396 : *
1397 : * \param[in,out] scheduler Scheduler data
1398 : */
1399 : void
1400 0 : pcmk__output_actions(pcmk_scheduler_t *scheduler)
1401 : {
1402 0 : pcmk__output_t *out = scheduler->priv;
1403 :
1404 : // Output node (non-resource) actions
1405 0 : for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
1406 0 : char *node_name = NULL;
1407 0 : char *task = NULL;
1408 0 : pcmk_action_t *action = (pcmk_action_t *) iter->data;
1409 :
1410 0 : if (action->rsc != NULL) {
1411 0 : continue; // Resource actions will be output later
1412 :
1413 0 : } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
1414 0 : continue; // This action was not scheduled
1415 : }
1416 :
1417 0 : if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN,
1418 : pcmk__str_none)) {
1419 0 : task = strdup("Shutdown");
1420 :
1421 0 : } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
1422 : pcmk__str_none)) {
1423 0 : const char *op = g_hash_table_lookup(action->meta,
1424 : PCMK__META_STONITH_ACTION);
1425 :
1426 0 : task = crm_strdup_printf("Fence (%s)", op);
1427 :
1428 : } else {
1429 0 : continue; // Don't display other node action types
1430 : }
1431 :
1432 0 : if (pcmk__is_guest_or_bundle_node(action->node)) {
1433 0 : const pcmk_resource_t *remote = action->node->details->remote_rsc;
1434 :
1435 0 : node_name = crm_strdup_printf("%s (resource: %s)",
1436 0 : pcmk__node_name(action->node),
1437 0 : remote->container->id);
1438 0 : } else if (action->node != NULL) {
1439 0 : node_name = crm_strdup_printf("%s", pcmk__node_name(action->node));
1440 : }
1441 :
1442 0 : out->message(out, "node-action", task, node_name, action->reason);
1443 :
1444 0 : free(node_name);
1445 0 : free(task);
1446 : }
1447 :
1448 : // Output resource actions
1449 0 : for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
1450 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1451 :
1452 0 : rsc->cmds->output_actions(rsc);
1453 : }
1454 0 : }
1455 :
1456 : /*!
1457 : * \internal
1458 : * \brief Get action name needed to compare digest for configuration changes
1459 : *
1460 : * \param[in] task Action name from history
1461 : * \param[in] interval_ms Action interval (in milliseconds)
1462 : *
1463 : * \return Action name whose digest should be compared
1464 : */
1465 : static const char *
1466 0 : task_for_digest(const char *task, guint interval_ms)
1467 : {
1468 : /* Certain actions need to be compared against the parameters used to start
1469 : * the resource.
1470 : */
1471 0 : if ((interval_ms == 0)
1472 0 : && pcmk__str_any_of(task, PCMK_ACTION_MONITOR, PCMK_ACTION_MIGRATE_FROM,
1473 : PCMK_ACTION_PROMOTE, NULL)) {
1474 0 : task = PCMK_ACTION_START;
1475 : }
1476 0 : return task;
1477 : }
1478 :
1479 : /*!
1480 : * \internal
1481 : * \brief Check whether only sanitized parameters to an action changed
1482 : *
1483 : * When collecting CIB files for troubleshooting, crm_report will mask
1484 : * sensitive resource parameters. If simulations were run using that, affected
1485 : * resources would appear to need a restart, which would complicate
1486 : * troubleshooting. To avoid that, we save a "secure digest" of non-sensitive
1487 : * parameters. This function used that digest to check whether only masked
1488 : * parameters are different.
1489 : *
1490 : * \param[in] xml_op Resource history entry with secure digest
1491 : * \param[in] digest_data Operation digest information being compared
1492 : * \param[in] scheduler Scheduler data
1493 : *
1494 : * \return true if only sanitized parameters changed, otherwise false
1495 : */
1496 : static bool
1497 0 : only_sanitized_changed(const xmlNode *xml_op,
1498 : const pcmk__op_digest_t *digest_data,
1499 : const pcmk_scheduler_t *scheduler)
1500 : {
1501 0 : const char *digest_secure = NULL;
1502 :
1503 0 : if (!pcmk_is_set(scheduler->flags, pcmk_sched_sanitized)) {
1504 : // The scheduler is not being run as a simulation
1505 0 : return false;
1506 : }
1507 :
1508 0 : digest_secure = crm_element_value(xml_op, PCMK__XA_OP_SECURE_DIGEST);
1509 :
1510 0 : return (digest_data->rc != pcmk__digest_match) && (digest_secure != NULL)
1511 0 : && (digest_data->digest_secure_calc != NULL)
1512 0 : && (strcmp(digest_data->digest_secure_calc, digest_secure) == 0);
1513 : }
1514 :
1515 : /*!
1516 : * \internal
1517 : * \brief Force a restart due to a configuration change
1518 : *
1519 : * \param[in,out] rsc Resource that action is for
1520 : * \param[in] task Name of action whose configuration changed
1521 : * \param[in] interval_ms Action interval (in milliseconds)
1522 : * \param[in,out] node Node where resource should be restarted
1523 : */
1524 : static void
1525 0 : force_restart(pcmk_resource_t *rsc, const char *task, guint interval_ms,
1526 : pcmk_node_t *node)
1527 : {
1528 0 : char *key = pcmk__op_key(rsc->id, task, interval_ms);
1529 0 : pcmk_action_t *required = custom_action(rsc, key, task, NULL, FALSE,
1530 : rsc->cluster);
1531 :
1532 0 : pe_action_set_reason(required, "resource definition change", true);
1533 0 : trigger_unfencing(rsc, node, "Device parameters changed", NULL,
1534 : rsc->cluster);
1535 0 : }
1536 :
1537 : /*!
1538 : * \internal
1539 : * \brief Schedule a reload of a resource on a node
1540 : *
1541 : * \param[in,out] data Resource to reload
1542 : * \param[in] user_data Where resource should be reloaded
1543 : */
1544 : static void
1545 0 : schedule_reload(gpointer data, gpointer user_data)
1546 : {
1547 0 : pcmk_resource_t *rsc = data;
1548 0 : const pcmk_node_t *node = user_data;
1549 0 : pcmk_action_t *reload = NULL;
1550 :
1551 : // For collective resources, just call recursively for children
1552 0 : if (rsc->variant > pcmk_rsc_variant_primitive) {
1553 0 : g_list_foreach(rsc->children, schedule_reload, user_data);
1554 0 : return;
1555 : }
1556 :
1557 : // Skip the reload in certain situations
1558 0 : if ((node == NULL)
1559 0 : || !pcmk_is_set(rsc->flags, pcmk_rsc_managed)
1560 0 : || pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
1561 0 : pcmk__rsc_trace(rsc, "Skip reload of %s:%s%s %s",
1562 : rsc->id,
1563 : pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " unmanaged",
1564 : pcmk_is_set(rsc->flags, pcmk_rsc_failed)? " failed" : "",
1565 : (node == NULL)? "inactive" : node->details->uname);
1566 0 : return;
1567 : }
1568 :
1569 : /* If a resource's configuration changed while a start was pending,
1570 : * force a full restart instead of a reload.
1571 : */
1572 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) {
1573 0 : pcmk__rsc_trace(rsc,
1574 : "%s: preventing agent reload because start pending",
1575 : rsc->id);
1576 0 : custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
1577 : rsc->cluster);
1578 0 : return;
1579 : }
1580 :
1581 : // Schedule the reload
1582 0 : pcmk__set_rsc_flags(rsc, pcmk_rsc_reload);
1583 0 : reload = custom_action(rsc, reload_key(rsc), PCMK_ACTION_RELOAD_AGENT, node,
1584 : FALSE, rsc->cluster);
1585 0 : pe_action_set_reason(reload, "resource definition change", FALSE);
1586 :
1587 : // Set orderings so that a required stop or demote cancels the reload
1588 0 : pcmk__new_ordering(NULL, NULL, reload, rsc, stop_key(rsc), NULL,
1589 : pcmk__ar_ordered|pcmk__ar_then_cancels_first,
1590 : rsc->cluster);
1591 0 : pcmk__new_ordering(NULL, NULL, reload, rsc, demote_key(rsc), NULL,
1592 : pcmk__ar_ordered|pcmk__ar_then_cancels_first,
1593 : rsc->cluster);
1594 : }
1595 :
1596 : /*!
1597 : * \internal
1598 : * \brief Handle any configuration change for an action
1599 : *
1600 : * Given an action from resource history, if the resource's configuration
1601 : * changed since the action was done, schedule any actions needed (restart,
1602 : * reload, unfencing, rescheduling recurring actions, etc.).
1603 : *
1604 : * \param[in,out] rsc Resource that action is for
1605 : * \param[in,out] node Node that action was on
1606 : * \param[in] xml_op Action XML from resource history
1607 : *
1608 : * \return true if action configuration changed, otherwise false
1609 : */
1610 : bool
1611 0 : pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node,
1612 : const xmlNode *xml_op)
1613 : {
1614 0 : guint interval_ms = 0;
1615 0 : const char *task = NULL;
1616 0 : const pcmk__op_digest_t *digest_data = NULL;
1617 :
1618 0 : CRM_CHECK((rsc != NULL) && (node != NULL) && (xml_op != NULL),
1619 : return false);
1620 :
1621 0 : task = crm_element_value(xml_op, PCMK_XA_OPERATION);
1622 0 : CRM_CHECK(task != NULL, return false);
1623 :
1624 0 : crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
1625 :
1626 : // If this is a recurring action, check whether it has been orphaned
1627 0 : if (interval_ms > 0) {
1628 0 : if (pcmk__find_action_config(rsc, task, interval_ms, false) != NULL) {
1629 0 : pcmk__rsc_trace(rsc,
1630 : "%s-interval %s for %s on %s is in configuration",
1631 : pcmk__readable_interval(interval_ms), task, rsc->id,
1632 : pcmk__node_name(node));
1633 0 : } else if (pcmk_is_set(rsc->cluster->flags,
1634 : pcmk_sched_cancel_removed_actions)) {
1635 0 : pcmk__schedule_cancel(rsc,
1636 : crm_element_value(xml_op, PCMK__XA_CALL_ID),
1637 : task, interval_ms, node, "orphan");
1638 0 : return true;
1639 : } else {
1640 0 : pcmk__rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned",
1641 : pcmk__readable_interval(interval_ms), task, rsc->id,
1642 : pcmk__node_name(node));
1643 0 : return true;
1644 : }
1645 : }
1646 :
1647 0 : crm_trace("Checking %s-interval %s for %s on %s for configuration changes",
1648 : pcmk__readable_interval(interval_ms), task, rsc->id,
1649 : pcmk__node_name(node));
1650 0 : task = task_for_digest(task, interval_ms);
1651 0 : digest_data = rsc_action_digest_cmp(rsc, xml_op, node, rsc->cluster);
1652 :
1653 0 : if (only_sanitized_changed(xml_op, digest_data, rsc->cluster)) {
1654 0 : if (!pcmk__is_daemon && (rsc->cluster->priv != NULL)) {
1655 0 : pcmk__output_t *out = rsc->cluster->priv;
1656 :
1657 0 : out->info(out,
1658 : "Only 'private' parameters to %s-interval %s for %s "
1659 : "on %s changed: %s",
1660 : pcmk__readable_interval(interval_ms), task, rsc->id,
1661 : pcmk__node_name(node),
1662 : crm_element_value(xml_op, PCMK__XA_TRANSITION_MAGIC));
1663 : }
1664 0 : return false;
1665 : }
1666 :
1667 0 : switch (digest_data->rc) {
1668 0 : case pcmk__digest_restart:
1669 0 : crm_log_xml_debug(digest_data->params_restart, "params:restart");
1670 0 : force_restart(rsc, task, interval_ms, node);
1671 0 : return true;
1672 :
1673 0 : case pcmk__digest_unknown:
1674 : case pcmk__digest_mismatch:
1675 : // Changes that can potentially be handled by an agent reload
1676 :
1677 0 : if (interval_ms > 0) {
1678 : /* Recurring actions aren't reloaded per se, they are just
1679 : * re-scheduled so the next run uses the new parameters.
1680 : * The old instance will be cancelled automatically.
1681 : */
1682 0 : crm_log_xml_debug(digest_data->params_all, "params:reschedule");
1683 0 : pcmk__reschedule_recurring(rsc, task, interval_ms, node);
1684 :
1685 0 : } else if (crm_element_value(xml_op,
1686 : PCMK__XA_OP_RESTART_DIGEST) != NULL) {
1687 : // Agent supports reload, so use it
1688 0 : trigger_unfencing(rsc, node,
1689 : "Device parameters changed (reload)", NULL,
1690 : rsc->cluster);
1691 0 : crm_log_xml_debug(digest_data->params_all, "params:reload");
1692 0 : schedule_reload((gpointer) rsc, (gpointer) node);
1693 :
1694 : } else {
1695 0 : pcmk__rsc_trace(rsc,
1696 : "Restarting %s "
1697 : "because agent doesn't support reload",
1698 : rsc->id);
1699 0 : crm_log_xml_debug(digest_data->params_restart,
1700 : "params:restart");
1701 0 : force_restart(rsc, task, interval_ms, node);
1702 : }
1703 0 : return true;
1704 :
1705 0 : default:
1706 0 : break;
1707 : }
1708 0 : return false;
1709 : }
1710 :
1711 : /*!
1712 : * \internal
1713 : * \brief Create a list of resource's action history entries, sorted by call ID
1714 : *
1715 : * \param[in] rsc_entry Resource's \c PCMK__XE_LRM_RSC_OP status XML
1716 : * \param[out] start_index Where to store index of start-like action, if any
1717 : * \param[out] stop_index Where to store index of stop action, if any
1718 : */
1719 : static GList *
1720 0 : rsc_history_as_list(const xmlNode *rsc_entry, int *start_index, int *stop_index)
1721 : {
1722 0 : GList *ops = NULL;
1723 :
1724 0 : for (xmlNode *rsc_op = pcmk__xe_first_child(rsc_entry, PCMK__XE_LRM_RSC_OP,
1725 : NULL, NULL);
1726 0 : rsc_op != NULL; rsc_op = pcmk__xe_next_same(rsc_op)) {
1727 :
1728 0 : ops = g_list_prepend(ops, rsc_op);
1729 : }
1730 0 : ops = g_list_sort(ops, sort_op_by_callid);
1731 0 : calculate_active_ops(ops, start_index, stop_index);
1732 0 : return ops;
1733 : }
1734 :
1735 : /*!
1736 : * \internal
1737 : * \brief Process a resource's action history from the CIB status
1738 : *
1739 : * Given a resource's action history, if the resource's configuration
1740 : * changed since the actions were done, schedule any actions needed (restart,
1741 : * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1742 : * (This also cancels recurring actions for maintenance mode, which is not
1743 : * entirely related but convenient to do here.)
1744 : *
1745 : * \param[in] rsc_entry Resource's \c PCMK__XE_LRM_RSC_OP status XML
1746 : * \param[in,out] rsc Resource whose history is being processed
1747 : * \param[in,out] node Node whose history is being processed
1748 : */
1749 : static void
1750 0 : process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc,
1751 : pcmk_node_t *node)
1752 : {
1753 0 : int offset = -1;
1754 0 : int stop_index = 0;
1755 0 : int start_index = 0;
1756 0 : GList *sorted_op_list = NULL;
1757 :
1758 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
1759 0 : if (pcmk__is_anonymous_clone(pe__const_top_resource(rsc, false))) {
1760 0 : pcmk__rsc_trace(rsc,
1761 : "Skipping configuration check "
1762 : "for orphaned clone instance %s",
1763 : rsc->id);
1764 : } else {
1765 0 : pcmk__rsc_trace(rsc,
1766 : "Skipping configuration check and scheduling "
1767 : "clean-up for orphaned resource %s", rsc->id);
1768 0 : pcmk__schedule_cleanup(rsc, node, false);
1769 : }
1770 0 : return;
1771 : }
1772 :
1773 0 : if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
1774 0 : if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, false)) {
1775 0 : pcmk__schedule_cleanup(rsc, node, false);
1776 : }
1777 0 : pcmk__rsc_trace(rsc,
1778 : "Skipping configuration check for %s "
1779 : "because no longer active on %s",
1780 : rsc->id, pcmk__node_name(node));
1781 0 : return;
1782 : }
1783 :
1784 0 : pcmk__rsc_trace(rsc, "Checking for configuration changes for %s on %s",
1785 : rsc->id, pcmk__node_name(node));
1786 :
1787 0 : if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, true)) {
1788 0 : pcmk__schedule_cleanup(rsc, node, false);
1789 : }
1790 :
1791 0 : sorted_op_list = rsc_history_as_list(rsc_entry, &start_index, &stop_index);
1792 0 : if (start_index < stop_index) {
1793 0 : return; // Resource is stopped
1794 : }
1795 :
1796 0 : for (GList *iter = sorted_op_list; iter != NULL; iter = iter->next) {
1797 0 : xmlNode *rsc_op = (xmlNode *) iter->data;
1798 0 : const char *task = NULL;
1799 0 : guint interval_ms = 0;
1800 :
1801 0 : if (++offset < start_index) {
1802 : // Skip actions that happened before a start
1803 0 : continue;
1804 : }
1805 :
1806 0 : task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
1807 0 : crm_element_value_ms(rsc_op, PCMK_META_INTERVAL, &interval_ms);
1808 :
1809 0 : if ((interval_ms > 0)
1810 0 : && (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)
1811 0 : || node->details->maintenance)) {
1812 : // Maintenance mode cancels recurring operations
1813 0 : pcmk__schedule_cancel(rsc,
1814 : crm_element_value(rsc_op, PCMK__XA_CALL_ID),
1815 : task, interval_ms, node, "maintenance mode");
1816 :
1817 0 : } else if ((interval_ms > 0)
1818 0 : || pcmk__strcase_any_of(task, PCMK_ACTION_MONITOR,
1819 : PCMK_ACTION_START,
1820 : PCMK_ACTION_PROMOTE,
1821 : PCMK_ACTION_MIGRATE_FROM, NULL)) {
1822 : /* If a resource operation failed, and the operation's definition
1823 : * has changed, clear any fail count so they can be retried fresh.
1824 : */
1825 :
1826 0 : if (pe__bundle_needs_remote_name(rsc)) {
1827 : /* We haven't assigned resources to nodes yet, so if the
1828 : * REMOTE_CONTAINER_HACK is used, we may calculate the digest
1829 : * based on the literal "#uname" value rather than the properly
1830 : * substituted value. That would mistakenly make the action
1831 : * definition appear to have been changed. Defer the check until
1832 : * later in this case.
1833 : */
1834 0 : pe__add_param_check(rsc_op, rsc, node, pcmk__check_active,
1835 : rsc->cluster);
1836 :
1837 0 : } else if (pcmk__check_action_config(rsc, node, rsc_op)
1838 0 : && (pe_get_failcount(node, rsc, NULL, pcmk__fc_effective,
1839 : NULL) != 0)) {
1840 0 : pe__clear_failcount(rsc, node, "action definition changed",
1841 : rsc->cluster);
1842 : }
1843 : }
1844 : }
1845 0 : g_list_free(sorted_op_list);
1846 : }
1847 :
1848 : /*!
1849 : * \internal
1850 : * \brief Process a node's action history from the CIB status
1851 : *
1852 : * Given a node's resource history, if the resource's configuration changed
1853 : * since the actions were done, schedule any actions needed (restart,
1854 : * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1855 : * (This also cancels recurring actions for maintenance mode, which is not
1856 : * entirely related but convenient to do here.)
1857 : *
1858 : * \param[in,out] node Node whose history is being processed
1859 : * \param[in] lrm_rscs Node's \c PCMK__XE_LRM_RESOURCES from CIB status XML
1860 : */
1861 : static void
1862 0 : process_node_history(pcmk_node_t *node, const xmlNode *lrm_rscs)
1863 : {
1864 0 : crm_trace("Processing node history for %s", pcmk__node_name(node));
1865 0 : for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rscs,
1866 : PCMK__XE_LRM_RESOURCE,
1867 : NULL, NULL);
1868 0 : rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) {
1869 :
1870 0 : if (rsc_entry->children != NULL) {
1871 0 : GList *result = pcmk__rscs_matching_id(pcmk__xe_id(rsc_entry),
1872 0 : node->details->data_set);
1873 :
1874 0 : for (GList *iter = result; iter != NULL; iter = iter->next) {
1875 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1876 :
1877 0 : if (rsc->variant == pcmk_rsc_variant_primitive) {
1878 0 : process_rsc_history(rsc_entry, rsc, node);
1879 : }
1880 : }
1881 0 : g_list_free(result);
1882 : }
1883 : }
1884 0 : }
1885 :
1886 : // XPath to find a node's resource history
1887 : #define XPATH_NODE_HISTORY "/" PCMK_XE_CIB "/" PCMK_XE_STATUS \
1888 : "/" PCMK__XE_NODE_STATE \
1889 : "[@" PCMK_XA_UNAME "='%s']" \
1890 : "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES
1891 :
1892 : /*!
1893 : * \internal
1894 : * \brief Process any resource configuration changes in the CIB status
1895 : *
1896 : * Go through all nodes' resource history, and if a resource's configuration
1897 : * changed since its actions were done, schedule any actions needed (restart,
1898 : * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1899 : * (This also cancels recurring actions for maintenance mode, which is not
1900 : * entirely related but convenient to do here.)
1901 : *
1902 : * \param[in,out] scheduler Scheduler data
1903 : */
1904 : void
1905 0 : pcmk__handle_rsc_config_changes(pcmk_scheduler_t *scheduler)
1906 : {
1907 0 : crm_trace("Check resource and action configuration for changes");
1908 :
1909 : /* Rather than iterate through the status section, iterate through the nodes
1910 : * and search for the appropriate status subsection for each. This skips
1911 : * orphaned nodes and lets us eliminate some cases before searching the XML.
1912 : */
1913 0 : for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
1914 0 : pcmk_node_t *node = (pcmk_node_t *) iter->data;
1915 :
1916 : /* Don't bother checking actions for a node that can't run actions ...
1917 : * unless it's in maintenance mode, in which case we still need to
1918 : * cancel any existing recurring monitors.
1919 : */
1920 0 : if (node->details->maintenance
1921 0 : || pcmk__node_available(node, false, false)) {
1922 :
1923 0 : char *xpath = NULL;
1924 0 : xmlNode *history = NULL;
1925 :
1926 0 : xpath = crm_strdup_printf(XPATH_NODE_HISTORY, node->details->uname);
1927 0 : history = get_xpath_object(xpath, scheduler->input, LOG_NEVER);
1928 0 : free(xpath);
1929 :
1930 0 : process_node_history(node, history);
1931 : }
1932 : }
1933 0 : }
|