Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4624132
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
85 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pacemaker/Makefile.am b/lib/pacemaker/Makefile.am
index b02a412a2a..dd75c06889 100644
--- a/lib/pacemaker/Makefile.am
+++ b/lib/pacemaker/Makefile.am
@@ -1,60 +1,61 @@
#
# Copyright 2004-2021 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
include $(top_srcdir)/mk/common.mk
AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
noinst_HEADERS = libpacemaker_private.h
## libraries
lib_LTLIBRARIES = libpacemaker.la
## SOURCES
libpacemaker_la_LDFLAGS = -version-info 4:0:3
libpacemaker_la_CFLAGS = $(CFLAGS_HARDENED_LIB)
libpacemaker_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB)
libpacemaker_la_LIBADD = $(top_builddir)/lib/pengine/libpe_status.la \
$(top_builddir)/lib/cib/libcib.la \
$(top_builddir)/lib/lrmd/liblrmd.la \
$(top_builddir)/lib/common/libcrmcommon.la
# -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
# Use += rather than backlashed continuation lines for parsing by bumplibs
libpacemaker_la_SOURCES =
libpacemaker_la_SOURCES += pcmk_cluster_queries.c
libpacemaker_la_SOURCES += pcmk_fence.c
libpacemaker_la_SOURCES += pcmk_graph_consumer.c
libpacemaker_la_SOURCES += pcmk_graph_logging.c
libpacemaker_la_SOURCES += pcmk_graph_producer.c
libpacemaker_la_SOURCES += pcmk_output.c
libpacemaker_la_SOURCES += pcmk_output_utils.c
libpacemaker_la_SOURCES += pcmk_resource.c
+libpacemaker_la_SOURCES += pcmk_sched_actions.c
libpacemaker_la_SOURCES += pcmk_sched_allocate.c
libpacemaker_la_SOURCES += pcmk_sched_bundle.c
libpacemaker_la_SOURCES += pcmk_sched_clone.c
libpacemaker_la_SOURCES += pcmk_sched_colocation.c
libpacemaker_la_SOURCES += pcmk_sched_constraints.c
libpacemaker_la_SOURCES += pcmk_sched_fencing.c
libpacemaker_la_SOURCES += pcmk_sched_group.c
libpacemaker_la_SOURCES += pcmk_sched_location.c
libpacemaker_la_SOURCES += pcmk_sched_messages.c
libpacemaker_la_SOURCES += pcmk_sched_native.c
libpacemaker_la_SOURCES += pcmk_sched_notif.c
libpacemaker_la_SOURCES += pcmk_sched_ordering.c
libpacemaker_la_SOURCES += pcmk_sched_promotable.c
libpacemaker_la_SOURCES += pcmk_sched_remote.c
libpacemaker_la_SOURCES += pcmk_sched_resource.c
libpacemaker_la_SOURCES += pcmk_sched_tickets.c
libpacemaker_la_SOURCES += pcmk_sched_transition.c
libpacemaker_la_SOURCES += pcmk_sched_utilization.c
libpacemaker_la_SOURCES += pcmk_sched_utils.c
libpacemaker_la_SOURCES += pcmk_simulate.c
diff --git a/lib/pacemaker/pcmk_graph_producer.c b/lib/pacemaker/pcmk_graph_producer.c
index 5bec9d8cea..7061f37d20 100644
--- a/lib/pacemaker/pcmk_graph_producer.c
+++ b/lib/pacemaker/pcmk_graph_producer.c
@@ -1,1558 +1,988 @@
/*
* Copyright 2004-2021 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <glib.h>
#include <pacemaker-internal.h>
#include "libpacemaker_private.h"
-gboolean rsc_update_action(pe_action_t * first, pe_action_t * then, enum pe_ordering type);
-
-static enum pe_action_flags
-get_action_flags(pe_action_t * action, pe_node_t * node)
-{
- enum pe_action_flags flags = action->flags;
-
- if (action->rsc) {
- flags = action->rsc->cmds->action_flags(action, NULL);
-
- if (pe_rsc_is_clone(action->rsc) && node) {
-
- /* We only care about activity on $node */
- enum pe_action_flags clone_flags = action->rsc->cmds->action_flags(action, node);
-
- /* Go to great lengths to ensure the correct value for pe_action_runnable...
- *
- * If we are a clone, then for _ordering_ constraints, it's only relevant
- * if we are runnable _anywhere_.
- *
- * This only applies to _runnable_ though, and only for ordering constraints.
- * If this function is ever used during colocation, then we'll need additional logic
- *
- * Not very satisfying, but it's logical and appears to work well.
- */
- if (!pcmk_is_set(clone_flags, pe_action_runnable)
- && pcmk_is_set(flags, pe_action_runnable)) {
-
- pe__set_raw_action_flags(clone_flags, action->rsc->id,
- pe_action_runnable);
- }
- flags = clone_flags;
- }
- }
- return flags;
-}
-
-static char *
-convert_non_atomic_uuid(char *old_uuid, pe_resource_t * rsc, gboolean allow_notify,
- gboolean free_original)
-{
- guint interval_ms = 0;
- char *uuid = NULL;
- char *rid = NULL;
- char *raw_task = NULL;
- int task = no_action;
-
- CRM_ASSERT(rsc);
- pe_rsc_trace(rsc, "Processing %s", old_uuid);
- if (old_uuid == NULL) {
- return NULL;
-
- } else if (strstr(old_uuid, "notify") != NULL) {
- goto done; /* no conversion */
-
- } else if (rsc->variant < pe_group) {
- goto done; /* no conversion */
- }
-
- CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval_ms));
- if (interval_ms > 0) {
- goto done; /* no conversion */
- }
-
- task = text2task(raw_task);
- switch (task) {
- case stop_rsc:
- case start_rsc:
- case action_notify:
- case action_promote:
- case action_demote:
- break;
- case stopped_rsc:
- case started_rsc:
- case action_notified:
- case action_promoted:
- case action_demoted:
- task--;
- break;
- case monitor_rsc:
- case shutdown_crm:
- case stonith_node:
- task = no_action;
- break;
- default:
- crm_err("Unknown action: %s", raw_task);
- task = no_action;
- break;
- }
-
- if (task != no_action) {
- if (pcmk_is_set(rsc->flags, pe_rsc_notify) && allow_notify) {
- uuid = pcmk__notify_key(rid, "confirmed-post", task2text(task + 1));
-
- } else {
- uuid = pcmk__op_key(rid, task2text(task + 1), 0);
- }
- pe_rsc_trace(rsc, "Converted %s -> %s", old_uuid, uuid);
- }
-
- done:
- if (uuid == NULL) {
- uuid = strdup(old_uuid);
- }
-
- if (free_original) {
- free(old_uuid);
- }
-
- free(raw_task);
- free(rid);
- return uuid;
-}
-
-static pe_action_t *
-rsc_expand_action(pe_action_t * action)
-{
- gboolean notify = FALSE;
- pe_action_t *result = action;
- pe_resource_t *rsc = action->rsc;
-
- if (rsc == NULL) {
- return action;
- }
-
- if ((rsc->parent == NULL)
- || (pe_rsc_is_clone(rsc) && (rsc->parent->variant == pe_container))) {
- /* Only outermost resources have notification actions.
- * The exception is those in bundles.
- */
- notify = pcmk_is_set(rsc->flags, pe_rsc_notify);
- }
-
- if (rsc->variant >= pe_group) {
- /* Expand 'start' -> 'started' */
- char *uuid = NULL;
-
- uuid = convert_non_atomic_uuid(action->uuid, rsc, notify, FALSE);
- if (uuid) {
- pe_rsc_trace(rsc, "Converting %s to %s %d", action->uuid, uuid,
- pcmk_is_set(rsc->flags, pe_rsc_notify));
- result = find_first_action(rsc->actions, uuid, NULL, NULL);
- if (result == NULL) {
- crm_err("Couldn't expand %s to %s in %s", action->uuid, uuid, rsc->id);
- result = action;
- }
- free(uuid);
- }
- }
- return result;
-}
-
-static enum pe_graph_flags
-graph_update_action(pe_action_t * first, pe_action_t * then, pe_node_t * node,
- enum pe_action_flags first_flags, enum pe_action_flags then_flags,
- pe_action_wrapper_t *order, pe_working_set_t *data_set)
-{
- enum pe_graph_flags changed = pe_graph_none;
- enum pe_ordering type = order->type;
-
- /* TODO: Do as many of these in parallel as possible */
-
- if (pcmk_is_set(type, pe_order_implies_then_on_node)) {
- /* Normally we want the _whole_ 'then' clone to
- * restart if 'first' is restarted, so then->node is
- * needed.
- *
- * However for unfencing, we want to limit this to
- * instances on the same node as 'first' (the
- * unfencing operation), so first->node is supplied.
- *
- * Swap the node, from then on we can can treat it
- * like any other 'pe_order_implies_then'
- */
-
- pe__clear_order_flags(type, pe_order_implies_then_on_node);
- pe__set_order_flags(type, pe_order_implies_then);
- node = first->node;
- pe_rsc_trace(then->rsc,
- "%s then %s: mapped pe_order_implies_then_on_node to "
- "pe_order_implies_then on %s",
- first->uuid, then->uuid, node->details->uname);
- }
-
- if (type & pe_order_implies_then) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags & pe_action_optional, pe_action_optional,
- pe_order_implies_then, data_set);
-
- } else if (!pcmk_is_set(first_flags, pe_action_optional)
- && pcmk_is_set(then->flags, pe_action_optional)) {
- pe__clear_action_flags(then, pe_action_optional);
- pe__set_graph_flags(changed, first, pe_graph_updated_then);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_then",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if ((type & pe_order_restart) && then->rsc) {
- enum pe_action_flags restart = (pe_action_optional | pe_action_runnable);
-
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, restart,
- pe_order_restart, data_set);
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_restart",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_implies_first) {
- if (first->rsc) {
- changed |= first->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_optional, pe_order_implies_first,
- data_set);
-
- } else if (!pcmk_is_set(first_flags, pe_action_optional)
- && pcmk_is_set(first->flags, pe_action_runnable)) {
- pe__clear_action_flags(first, pe_action_runnable);
- pe__set_graph_flags(changed, first, pe_graph_updated_first);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_first",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_promoted_implies_first) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags & pe_action_optional, pe_action_optional,
- pe_order_promoted_implies_first, data_set);
- }
- pe_rsc_trace(then->rsc,
- "%s then %s: %s after pe_order_promoted_implies_first",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_one_or_more) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_runnable, pe_order_one_or_more,
- data_set);
-
- } else if (pcmk_is_set(first_flags, pe_action_runnable)) {
- // We have another runnable instance of "first"
- then->runnable_before++;
-
- /* Mark "then" as runnable if it requires a certain number of
- * "before" instances to be runnable, and they now are.
- */
- if ((then->runnable_before >= then->required_runnable_before)
- && !pcmk_is_set(then->flags, pe_action_runnable)) {
-
- pe__set_action_flags(then, pe_action_runnable);
- pe__set_graph_flags(changed, first, pe_graph_updated_then);
- }
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_one_or_more",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (then->rsc && pcmk_is_set(type, pe_order_probe)) {
- if (!pcmk_is_set(first_flags, pe_action_runnable)
- && (first->rsc->running_on != NULL)) {
-
- pe_rsc_trace(then->rsc,
- "%s then %s: ignoring because first is stopping",
- first->uuid, then->uuid);
- type = pe_order_none;
- order->type = pe_order_none;
-
- } else {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_runnable, pe_order_runnable_left,
- data_set);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_probe",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_runnable_left) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_runnable, pe_order_runnable_left,
- data_set);
-
- } else if (!pcmk_is_set(first_flags, pe_action_runnable)
- && pcmk_is_set(then->flags, pe_action_runnable)) {
-
- pe__clear_action_flags(then, pe_action_runnable);
- pe__set_graph_flags(changed, first, pe_graph_updated_then);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_runnable_left",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_implies_first_migratable) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_optional,
- pe_order_implies_first_migratable, data_set);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after "
- "pe_order_implies_first_migratable",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_pseudo_left) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_optional, pe_order_pseudo_left,
- data_set);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_pseudo_left",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_optional) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_runnable, pe_order_optional, data_set);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_optional",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if (type & pe_order_asymmetrical) {
- if (then->rsc) {
- changed |= then->rsc->cmds->update_actions(first, then, node,
- first_flags, pe_action_runnable, pe_order_asymmetrical,
- data_set);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_asymmetrical",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- if ((first->flags & pe_action_runnable) && (type & pe_order_implies_then_printed)
- && (first_flags & pe_action_optional) == 0) {
- pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
- then->uuid, first->uuid);
- pe__set_action_flags(then, pe_action_print_always);
- // Don't bother marking 'then' as changed just for this
- }
-
- if (pcmk_is_set(type, pe_order_implies_first_printed)
- && !pcmk_is_set(then_flags, pe_action_optional)) {
-
- pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
- first->uuid, then->uuid);
- pe__set_action_flags(first, pe_action_print_always);
- // Don't bother marking 'first' as changed just for this
- }
-
- if ((type & pe_order_implies_then
- || type & pe_order_implies_first
- || type & pe_order_restart)
- && first->rsc
- && pcmk__str_eq(first->task, RSC_STOP, pcmk__str_casei)
- && !pcmk_is_set(first->rsc->flags, pe_rsc_managed)
- && pcmk_is_set(first->rsc->flags, pe_rsc_block)
- && !pcmk_is_set(first->flags, pe_action_runnable)) {
-
- if (pcmk_is_set(then->flags, pe_action_runnable)) {
- pe__clear_action_flags(then, pe_action_runnable);
- pe__set_graph_flags(changed, first, pe_graph_updated_then);
- }
- pe_rsc_trace(then->rsc, "%s then %s: %s after checking whether first "
- "is blocked, unmanaged, unrunnable stop",
- first->uuid, then->uuid,
- (changed? "changed" : "unchanged"));
- }
-
- return changed;
-}
-
// Convenience macros for logging action properties
#define action_type_str(flags) \
(pcmk_is_set((flags), pe_action_pseudo)? "pseudo-action" : "action")
#define action_optional_str(flags) \
(pcmk_is_set((flags), pe_action_optional)? "optional" : "required")
#define action_runnable_str(flags) \
(pcmk_is_set((flags), pe_action_runnable)? "runnable" : "unrunnable")
#define action_node_str(a) \
(((a)->node == NULL)? "no node" : (a)->node->details->uname)
-gboolean
-update_action(pe_action_t *then, pe_working_set_t *data_set)
-{
- GList *lpc = NULL;
- enum pe_graph_flags changed = pe_graph_none;
- int last_flags = then->flags;
-
- pe_rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
- action_type_str(then->flags), then->uuid,
- action_optional_str(then->flags),
- action_runnable_str(then->flags), action_node_str(then));
-
- if (pcmk_is_set(then->flags, pe_action_requires_any)) {
- /* initialize current known runnable before actions to 0
- * from here as graph_update_action is called for each of
- * then's before actions, this number will increment as
- * runnable 'first' actions are encountered */
- then->runnable_before = 0;
-
- /* for backwards compatibility with previous options that use
- * the 'requires_any' flag, initialize required to 1 if it is
- * not set. */
- if (then->required_runnable_before == 0) {
- then->required_runnable_before = 1;
- }
- pe__clear_action_flags(then, pe_action_runnable);
- /* We are relying on the pe_order_one_or_more clause of
- * graph_update_action(), called as part of the:
- *
- * 'if (first == other->action)'
- *
- * block below, to set this back if appropriate
- */
- }
-
- for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
- pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
- pe_action_t *first = other->action;
-
- pe_node_t *then_node = then->node;
- pe_node_t *first_node = first->node;
-
- enum pe_action_flags then_flags = 0;
- enum pe_action_flags first_flags = 0;
-
- if (first->rsc && first->rsc->variant == pe_group && pcmk__str_eq(first->task, RSC_START, pcmk__str_casei)) {
- first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
- if (first_node) {
- pe_rsc_trace(first->rsc, "Found node %s for 'first' %s",
- first_node->details->uname, first->uuid);
- }
- }
-
- if (then->rsc && then->rsc->variant == pe_group && pcmk__str_eq(then->task, RSC_START, pcmk__str_casei)) {
- then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
- if (then_node) {
- pe_rsc_trace(then->rsc, "Found node %s for 'then' %s",
- then_node->details->uname, then->uuid);
- }
- }
- /* Disable constraint if it only applies when on same node, but isn't */
- if (pcmk_is_set(other->type, pe_order_same_node)
- && (first_node != NULL) && (then_node != NULL)
- && (first_node->details != then_node->details)) {
-
- pe_rsc_trace(then->rsc,
- "Disabled ordering %s on %s then %s on %s: not same node",
- other->action->uuid, first_node->details->uname,
- then->uuid, then_node->details->uname);
- other->type = pe_order_none;
- continue;
- }
-
- pe__clear_graph_flags(changed, then, pe_graph_updated_first);
-
- if (first->rsc && pcmk_is_set(other->type, pe_order_then_cancels_first)
- && !pcmk_is_set(then->flags, pe_action_optional)) {
-
- /* 'then' is required, so we must abandon 'first'
- * (e.g. a required stop cancels any agent reload).
- */
- pe__set_action_flags(other->action, pe_action_optional);
- if (!strcmp(first->task, CRMD_ACTION_RELOAD_AGENT)) {
- pe__clear_resource_flags(first->rsc, pe_rsc_reload);
- }
- }
-
- if (first->rsc && then->rsc && (first->rsc != then->rsc)
- && (is_parent(then->rsc, first->rsc) == FALSE)) {
- first = rsc_expand_action(first);
- }
- if (first != other->action) {
- pe_rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
- then->uuid, first->uuid, other->action->uuid);
- }
-
- first_flags = get_action_flags(first, then_node);
- then_flags = get_action_flags(then, first_node);
-
- pe_rsc_trace(then->rsc,
- "%s then %s: type=0x%.6x filter=0x%.6x "
- "(%s %s %s on %s 0x%.6x then 0x%.6x)",
- first->uuid, then->uuid, other->type, first_flags,
- action_optional_str(first_flags),
- action_runnable_str(first_flags),
- action_type_str(first_flags), action_node_str(first),
- first->flags, then->flags);
-
- if (first == other->action) {
- /*
- * 'first' was not expanded (e.g. from 'start' to 'running'), which could mean it:
- * - has no associated resource,
- * - was a primitive,
- * - was pre-expanded (e.g. 'running' instead of 'start')
- *
- * The third argument here to graph_update_action() is a node which is used under two conditions:
- * - Interleaving, in which case first->node and
- * then->node are equal (and NULL)
- * - If 'then' is a clone, to limit the scope of the
- * constraint to instances on the supplied node
- *
- */
- pe_node_t *node = then->node;
- changed |= graph_update_action(first, then, node, first_flags,
- then_flags, other, data_set);
-
- /* 'first' was for a complex resource (clone, group, etc),
- * create a new dependency if necessary
- */
- } else if (order_actions(first, then, other->type)) {
- /* This was the first time 'first' and 'then' were associated,
- * start again to get the new actions_before list
- */
- pe__set_graph_flags(changed, then,
- pe_graph_updated_then|pe_graph_disable);
- }
-
- if (changed & pe_graph_disable) {
- pe_rsc_trace(then->rsc,
- "Disabled ordering %s then %s in favor of %s then %s",
- other->action->uuid, then->uuid, first->uuid,
- then->uuid);
- pe__clear_graph_flags(changed, then, pe_graph_disable);
- other->type = pe_order_none;
- }
-
- if (changed & pe_graph_updated_first) {
- GList *lpc2 = NULL;
-
- crm_trace("Re-processing %s and its 'after' actions since it changed",
- first->uuid);
- for (lpc2 = first->actions_after; lpc2 != NULL; lpc2 = lpc2->next) {
- pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc2->data;
-
- update_action(other->action, data_set);
- }
- update_action(first, data_set);
- }
- }
-
- if (pcmk_is_set(then->flags, pe_action_requires_any)) {
- if (last_flags != then->flags) {
- pe__set_graph_flags(changed, then, pe_graph_updated_then);
- } else {
- pe__clear_graph_flags(changed, then, pe_graph_updated_then);
- }
- }
-
- if (changed & pe_graph_updated_then) {
- crm_trace("Re-processing %s and its 'after' actions since it changed",
- then->uuid);
- if (pcmk_is_set(last_flags, pe_action_runnable)
- && !pcmk_is_set(then->flags, pe_action_runnable)) {
- pcmk__block_colocated_starts(then, data_set);
- }
- update_action(then, data_set);
- for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
- pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
-
- update_action(other->action, data_set);
- }
- }
-
- return FALSE;
-}
-
/*!
* \internal
* \brief Add an XML node tag for a specified ID
*
* \param[in] id Node UUID to add
* \param[in,out] xml Parent XML tag to add to
*/
static xmlNode*
add_node_to_xml_by_id(const char *id, xmlNode *xml)
{
xmlNode *node_xml;
node_xml = create_xml_node(xml, XML_CIB_TAG_NODE);
crm_xml_add(node_xml, XML_ATTR_UUID, id);
return node_xml;
}
/*!
* \internal
* \brief Add an XML node tag for a specified node
*
* \param[in] node Node to add
* \param[in,out] xml XML to add node to
*/
static void
add_node_to_xml(const pe_node_t *node, void *xml)
{
add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
}
/*!
* \internal
* \brief Add XML with nodes that need an update of their maintenance state
*
* \param[in,out] xml Parent XML tag to add to
* \param[in] data_set Working set for cluster
*/
static int
add_maintenance_nodes(xmlNode *xml, const pe_working_set_t *data_set)
{
GList *gIter = NULL;
xmlNode *maintenance =
xml?create_xml_node(xml, XML_GRAPH_TAG_MAINTENANCE):NULL;
int count = 0;
for (gIter = data_set->nodes; gIter != NULL;
gIter = gIter->next) {
pe_node_t *node = (pe_node_t *) gIter->data;
struct pe_node_shared_s *details = node->details;
if (!pe__is_guest_or_remote_node(node)) {
continue; /* just remote nodes need to know atm */
}
if (details->maintenance != details->remote_maintenance) {
if (maintenance) {
crm_xml_add(
add_node_to_xml_by_id(node->details->id, maintenance),
XML_NODE_IS_MAINTENANCE, details->maintenance?"1":"0");
}
count++;
}
}
crm_trace("%s %d nodes to adjust maintenance-mode "
"to transition", maintenance?"Added":"Counted", count);
return count;
}
/*!
* \internal
* \brief Add pseudo action with nodes needing maintenance state update
*
* \param[in,out] data_set Working set for cluster
*/
void
add_maintenance_update(pe_working_set_t *data_set)
{
pe_action_t *action = NULL;
if (add_maintenance_nodes(NULL, data_set)) {
crm_trace("adding maintenance state update pseudo action");
action = get_pseudo_op(CRM_OP_MAINTENANCE_NODES, data_set);
pe__set_action_flags(action, pe_action_print_always);
}
}
/*!
* \internal
* \brief Add XML with nodes that an action is expected to bring down
*
* If a specified action is expected to bring any nodes down, add an XML block
* with their UUIDs. When a node is lost, this allows the controller to
* determine whether it was expected.
*
* \param[in,out] xml Parent XML tag to add to
* \param[in] action Action to check for downed nodes
* \param[in] data_set Working set for cluster
*/
static void
add_downed_nodes(xmlNode *xml, const pe_action_t *action,
const pe_working_set_t *data_set)
{
CRM_CHECK(xml && action && action->node && data_set, return);
if (pcmk__str_eq(action->task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
/* Shutdown makes the action's node down */
xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
add_node_to_xml_by_id(action->node->details->id, downed);
} else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
/* Fencing makes the action's node and any hosted guest nodes down */
const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
if (pcmk__is_fencing_action(fence)) {
xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
add_node_to_xml_by_id(action->node->details->id, downed);
pe_foreach_guest_node(data_set, action->node, add_node_to_xml, downed);
}
} else if (action->rsc && action->rsc->is_remote_node
&& pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
/* Stopping a remote connection resource makes connected node down,
* unless it's part of a migration
*/
GList *iter;
pe_action_t *input;
gboolean migrating = FALSE;
for (iter = action->actions_before; iter != NULL; iter = iter->next) {
input = ((pe_action_wrapper_t *) iter->data)->action;
if (input->rsc && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_casei)
&& pcmk__str_eq(input->task, CRMD_ACTION_MIGRATED, pcmk__str_casei)) {
migrating = TRUE;
break;
}
}
if (!migrating) {
xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
add_node_to_xml_by_id(action->rsc->id, downed);
}
}
}
static bool
should_lock_action(pe_action_t *action)
{
// Only actions taking place on resource's lock node are locked
if ((action->rsc->lock_node == NULL) || (action->node == NULL)
|| (action->node->details != action->rsc->lock_node->details)) {
return false;
}
/* During shutdown, only stops are locked (otherwise, another action such as
* a demote would cause the controller to clear the lock)
*/
if (action->node->details->shutdown && action->task
&& strcmp(action->task, RSC_STOP)) {
return false;
}
return true;
}
static xmlNode *
action2xml(pe_action_t * action, gboolean as_input, pe_working_set_t *data_set)
{
gboolean needs_node_info = TRUE;
gboolean needs_maintenance_info = FALSE;
xmlNode *action_xml = NULL;
xmlNode *args_xml = NULL;
#if ENABLE_VERSIONED_ATTRS
pe_rsc_action_details_t *rsc_details = NULL;
#endif
if (action == NULL) {
return NULL;
}
if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
/* All fences need node info; guest node fences are pseudo-events */
action_xml = create_xml_node(NULL,
pcmk_is_set(action->flags, pe_action_pseudo)?
XML_GRAPH_TAG_PSEUDO_EVENT :
XML_GRAPH_TAG_CRM_EVENT);
} else if (pcmk__str_eq(action->task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
} else if (pcmk__str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT, pcmk__str_casei)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
} else if (pcmk__str_eq(action->task, CRM_OP_LRM_REFRESH, pcmk__str_casei)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
} else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
// CIB-only clean-up for shutdown locks
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
crm_xml_add(action_xml, PCMK__XA_MODE, XML_TAG_CIB);
/* } else if(pcmk__str_eq(action->task, RSC_PROBED, pcmk__str_casei)) { */
/* action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */
} else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
if (pcmk__str_eq(action->task, CRM_OP_MAINTENANCE_NODES, pcmk__str_casei)) {
needs_maintenance_info = TRUE;
}
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT);
needs_node_info = FALSE;
} else {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
#if ENABLE_VERSIONED_ATTRS
rsc_details = pe_rsc_action_details(action);
#endif
}
crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
if (action->rsc != NULL && action->rsc->clone_name != NULL) {
char *clone_key = NULL;
guint interval_ms;
if (pcmk__guint_from_hash(action->meta,
XML_LRM_ATTR_INTERVAL_MS, 0,
&interval_ms) != pcmk_rc_ok) {
interval_ms = 0;
}
if (pcmk__str_eq(action->task, RSC_NOTIFY, pcmk__str_casei)) {
const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
const char *n_task = g_hash_table_lookup(action->meta, "notify_operation");
CRM_CHECK(n_type != NULL, crm_err("No notify type value found for %s", action->uuid));
CRM_CHECK(n_task != NULL,
crm_err("No notify operation value found for %s", action->uuid));
clone_key = pcmk__notify_key(action->rsc->clone_name,
n_type, n_task);
} else if(action->cancel_task) {
clone_key = pcmk__op_key(action->rsc->clone_name,
action->cancel_task, interval_ms);
} else {
clone_key = pcmk__op_key(action->rsc->clone_name,
action->task, interval_ms);
}
CRM_CHECK(clone_key != NULL, crm_err("Could not generate a key for %s", action->uuid));
crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, action->uuid);
free(clone_key);
} else {
crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
}
if (needs_node_info && action->node != NULL) {
pe_node_t *router_node = pcmk__connection_host_for_action(action);
crm_xml_add(action_xml, XML_LRM_ATTR_TARGET, action->node->details->uname);
crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
if (router_node) {
crm_xml_add(action_xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname);
}
g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET), strdup(action->node->details->uname));
g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID), strdup(action->node->details->id));
}
/* No details if this action is only being listed in the inputs section */
if (as_input) {
return action_xml;
}
if (action->rsc && !pcmk_is_set(action->flags, pe_action_pseudo)) {
int lpc = 0;
xmlNode *rsc_xml = NULL;
const char *attr_list[] = {
XML_AGENT_ATTR_CLASS,
XML_AGENT_ATTR_PROVIDER,
XML_ATTR_TYPE
};
/* If a resource is locked to a node via shutdown-lock, mark its actions
* so the controller can preserve the lock when the action completes.
*/
if (should_lock_action(action)) {
crm_xml_add_ll(action_xml, XML_CONFIG_ATTR_SHUTDOWN_LOCK,
(long long) action->rsc->lock_time);
}
// List affected resource
rsc_xml = create_xml_node(action_xml,
crm_element_name(action->rsc->xml));
if (pcmk_is_set(action->rsc->flags, pe_rsc_orphan)
&& action->rsc->clone_name) {
/* Do not use the 'instance free' name here as that
* might interfere with the instance we plan to keep.
* Ie. if there are more than two named /anonymous/
* instances on a given node, we need to make sure the
* command goes to the right one.
*
* Keep this block, even when everyone is using
* 'instance free' anonymous clone names - it means
* we'll do the right thing if anyone toggles the
* unique flag to 'off'
*/
crm_debug("Using orphan clone name %s instead of %s", action->rsc->id,
action->rsc->clone_name);
crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
} else if (!pcmk_is_set(action->rsc->flags, pe_rsc_unique)) {
const char *xml_id = ID(action->rsc->xml);
crm_debug("Using anonymous clone name %s for %s (aka. %s)", xml_id, action->rsc->id,
action->rsc->clone_name);
/* ID is what we'd like client to use
* ID_LONG is what they might know it as instead
*
* ID_LONG is only strictly needed /here/ during the
* transition period until all nodes in the cluster
* are running the new software /and/ have rebooted
* once (meaning that they've only ever spoken to a DC
* supporting this feature).
*
* If anyone toggles the unique flag to 'on', the
* 'instance free' name will correspond to an orphan
* and fall into the clause above instead
*/
crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
if (action->rsc->clone_name && !pcmk__str_eq(xml_id, action->rsc->clone_name, pcmk__str_casei)) {
crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
} else {
crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
}
} else {
CRM_ASSERT(action->rsc->clone_name == NULL);
crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
}
for (lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
crm_xml_add(rsc_xml, attr_list[lpc],
g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
}
}
/* List any attributes in effect */
args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
g_hash_table_foreach(action->extra, hash2field, args_xml);
if (action->rsc != NULL && action->node) {
// Get the resource instance attributes, evaluated properly for node
GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
pcmk__substitute_remote_addr(action->rsc, params, data_set);
g_hash_table_foreach(params, hash2smartfield, args_xml);
#if ENABLE_VERSIONED_ATTRS
{
xmlNode *versioned_parameters = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS);
pe_get_versioned_attributes(versioned_parameters, action->rsc,
action->node, data_set);
if (xml_has_children(versioned_parameters)) {
add_node_copy(action_xml, versioned_parameters);
}
free_xml(versioned_parameters);
}
#endif
} else if(action->rsc && action->rsc->variant <= pe_native) {
GHashTable *params = pe_rsc_params(action->rsc, NULL, data_set);
g_hash_table_foreach(params, hash2smartfield, args_xml);
#if ENABLE_VERSIONED_ATTRS
if (xml_has_children(action->rsc->versioned_parameters)) {
add_node_copy(action_xml, action->rsc->versioned_parameters);
}
#endif
}
#if ENABLE_VERSIONED_ATTRS
if (rsc_details) {
if (xml_has_children(rsc_details->versioned_parameters)) {
add_node_copy(action_xml, rsc_details->versioned_parameters);
}
if (xml_has_children(rsc_details->versioned_meta)) {
add_node_copy(action_xml, rsc_details->versioned_meta);
}
}
#endif
g_hash_table_foreach(action->meta, hash2metafield, args_xml);
if (action->rsc != NULL) {
const char *value = g_hash_table_lookup(action->rsc->meta, "external-ip");
pe_resource_t *parent = action->rsc;
while (parent != NULL) {
parent->cmds->append_meta(parent, args_xml);
parent = parent->parent;
}
if(value) {
hash2smartfield((gpointer)"pcmk_external_ip", (gpointer)value, (gpointer)args_xml);
}
pcmk__add_bundle_meta_to_xml(args_xml, action);
} else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei) && action->node) {
/* Pass the node's attributes as meta-attributes.
*
* @TODO: Determine whether it is still necessary to do this. It was
* added in 33d99707, probably for the libfence-based implementation in
* c9a90bd, which is no longer used.
*/
g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml);
}
sorted_xml(args_xml, action_xml, FALSE);
free_xml(args_xml);
/* List any nodes this action is expected to make down */
if (needs_node_info && (action->node != NULL)) {
add_downed_nodes(action_xml, action, data_set);
}
if (needs_maintenance_info) {
add_maintenance_nodes(action_xml, data_set);
}
crm_log_xml_trace(action_xml, "dumped action");
return action_xml;
}
static bool
should_dump_action(pe_action_t *action)
{
CRM_CHECK(action != NULL, return false);
if (pcmk_is_set(action->flags, pe_action_dumped)) {
crm_trace("Action %s (%d) already dumped", action->uuid, action->id);
return false;
} else if (pcmk_is_set(action->flags, pe_action_pseudo)
&& pcmk__str_eq(action->task, CRM_OP_PROBED, pcmk__str_casei)) {
GList *lpc = NULL;
/* This is a horrible but convenient hack
*
* It mimimizes the number of actions with unsatisfied inputs
* (i.e. not included in the graph)
*
* This in turn, means we can be more concise when printing
* aborted/incomplete graphs.
*
* It also makes it obvious which node is preventing
* probe_complete from running (presumably because it is only
* partially up)
*
* For these reasons we tolerate such perversions
*/
for (lpc = action->actions_after; lpc != NULL; lpc = lpc->next) {
pe_action_wrapper_t *wrapper = (pe_action_wrapper_t *) lpc->data;
if (!pcmk_is_set(wrapper->action->flags, pe_action_runnable)) {
/* Only interested in runnable operations */
} else if (!pcmk__str_eq(wrapper->action->task, RSC_START, pcmk__str_casei)) {
/* Only interested in start operations */
} else if (pcmk_is_set(wrapper->action->flags, pe_action_dumped)
|| should_dump_action(wrapper->action)) {
crm_trace("Action %s (%d) should be dumped: "
"dependency of %s (%d)",
action->uuid, action->id,
wrapper->action->uuid, wrapper->action->id);
return true;
}
}
}
if (!pcmk_is_set(action->flags, pe_action_runnable)) {
crm_trace("Ignoring action %s (%d): unrunnable",
action->uuid, action->id);
return false;
} else if (pcmk_is_set(action->flags, pe_action_optional)
&& !pcmk_is_set(action->flags, pe_action_print_always)) {
crm_trace("Ignoring action %s (%d): optional",
action->uuid, action->id);
return false;
// Monitors should be dumped even for unmanaged resources
} else if (action->rsc && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
&& !pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_casei)) {
const char *interval_ms_s = g_hash_table_lookup(action->meta,
XML_LRM_ATTR_INTERVAL_MS);
// Cancellation of recurring monitors should still be dumped
if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
action->uuid, action->id, action->rsc->id);
return false;
}
}
if (pcmk_is_set(action->flags, pe_action_pseudo) ||
pcmk__strcase_any_of(action->task, CRM_OP_FENCE, CRM_OP_SHUTDOWN, NULL)) {
/* skip the next checks */
return true;
}
if (action->node == NULL) {
pe_err("Skipping action %s (%d) "
"because it was not allocated to a node (bug?)",
action->uuid, action->id);
log_action(LOG_DEBUG, "Unallocated action", action, false);
return false;
} else if (pcmk_is_set(action->flags, pe_action_dc)) {
crm_trace("Action %s (%d) should be dumped: "
"can run on DC instead of %s",
action->uuid, action->id, action->node->details->uname);
} else if (pe__is_guest_node(action->node)
&& !action->node->details->remote_requires_reset) {
crm_trace("Action %s (%d) should be dumped: "
"assuming will be runnable on guest node %s",
action->uuid, action->id, action->node->details->uname);
} else if (action->node->details->online == false) {
pe_err("Skipping action %s (%d) "
"because it was scheduled for offline node (bug?)",
action->uuid, action->id);
log_action(LOG_DEBUG, "Action for offline node", action, FALSE);
return false;
#if 0
/* but this would also affect resources that can be safely
* migrated before a fencing op
*/
} else if (action->node->details->unclean == false) {
pe_err("Skipping action %s (%d) "
"because it was scheduled for unclean node (bug?)",
action->uuid, action->id);
log_action(LOG_DEBUG, "Action for unclean node", action, false);
return false;
#endif
}
return true;
}
/* lowest to highest */
static gint
sort_action_id(gconstpointer a, gconstpointer b)
{
const pe_action_wrapper_t *action_wrapper2 = (const pe_action_wrapper_t *)a;
const pe_action_wrapper_t *action_wrapper1 = (const pe_action_wrapper_t *)b;
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
if (action_wrapper1->action->id > action_wrapper2->action->id) {
return -1;
}
if (action_wrapper1->action->id < action_wrapper2->action->id) {
return 1;
}
return 0;
}
/*!
* \internal
* \brief Check whether an ordering's flags can change an action
*
* \param[in] ordering Ordering to check
*
* \return true if ordering has flags that can change an action, false otherwise
*/
static bool
ordering_can_change_actions(pe_action_wrapper_t *ordering)
{
return pcmk_any_flags_set(ordering->type, ~(pe_order_implies_first_printed
|pe_order_implies_then_printed
|pe_order_optional));
}
/*!
* \internal
* \brief Check whether an action input should be in the transition graph
*
* \param[in] action Action to check
* \param[in,out] input Action input to check
*
* \return true if input should be in graph, false otherwise
* \note This function may not only check an input, but disable it under certian
* circumstances (load or anti-colocation orderings that are not needed).
*/
static bool
check_dump_input(pe_action_t *action, pe_action_wrapper_t *input)
{
if (input->state == pe_link_dumped) {
return true;
}
if (input->type == pe_order_none) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"ordering disabled",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
&& !ordering_can_change_actions(input)
&& !pcmk__str_eq(input->action->uuid, CRM_OP_PROBED, pcmk__str_casei)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"optional and input unrunnable",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
&& pcmk_is_set(input->type, pe_order_one_or_more)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"one-or-more and input unrunnable",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if (pcmk_is_set(action->flags, pe_action_pseudo)
&& pcmk_is_set(input->type, pe_order_stonith_stop)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"stonith stop but action is pseudo",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if (pcmk_is_set(input->type, pe_order_implies_first_migratable)
&& !pcmk_is_set(input->action->flags, pe_action_runnable)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"implies input migratable but input unrunnable",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if (pcmk_is_set(input->type, pe_order_apply_first_non_migratable)
&& pcmk_is_set(input->action->flags, pe_action_migrate_runnable)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"only if input unmigratable but input unrunnable",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if ((input->type == pe_order_optional)
&& pcmk_is_set(input->action->flags, pe_action_migrate_runnable)
&& pcmk__ends_with(input->action->uuid, "_stop_0")) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"optional but stop in migration",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
} else if (input->type == pe_order_load) {
pe_node_t *input_node = input->action->node;
// load orderings are relevant only if actions are for same node
if (action->rsc && pcmk__str_eq(action->task, RSC_MIGRATE, pcmk__str_casei)) {
pe_node_t *allocated = action->rsc->allocated_to;
/* For load_stopped -> migrate_to orderings, we care about where it
* has been allocated to, not where it will be executed.
*/
if ((input_node == NULL) || (allocated == NULL)
|| (input_node->details != allocated->details)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"load ordering node mismatch %s vs %s",
action->uuid, action->id,
input->action->uuid, input->action->id,
(allocated? allocated->details->uname : "<none>"),
(input_node? input_node->details->uname : "<none>"));
input->type = pe_order_none;
return false;
}
} else if ((input_node == NULL) || (action->node == NULL)
|| (input_node->details != action->node->details)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"load ordering node mismatch %s vs %s",
action->uuid, action->id,
input->action->uuid, input->action->id,
(action->node? action->node->details->uname : "<none>"),
(input_node? input_node->details->uname : "<none>"));
input->type = pe_order_none;
return false;
} else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"load ordering input optional",
action->uuid, action->id,
input->action->uuid, input->action->id);
input->type = pe_order_none;
return false;
}
} else if (input->type == pe_order_anti_colocation) {
if (input->action->node && action->node
&& (input->action->node->details != action->node->details)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"anti-colocation node mismatch %s vs %s",
action->uuid, action->id,
input->action->uuid, input->action->id,
action->node->details->uname,
input->action->node->details->uname);
input->type = pe_order_none;
return false;
} else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"anti-colocation input optional",
action->uuid, action->id,
input->action->uuid, input->action->id);
input->type = pe_order_none;
return false;
}
} else if (input->action->rsc
&& input->action->rsc != action->rsc
&& pcmk_is_set(input->action->rsc->flags, pe_rsc_failed)
&& !pcmk_is_set(input->action->rsc->flags, pe_rsc_managed)
&& pcmk__ends_with(input->action->uuid, "_stop_0")
&& action->rsc && pe_rsc_is_clone(action->rsc)) {
crm_warn("Ignoring requirement that %s complete before %s:"
" unmanaged failed resources cannot prevent clone shutdown",
input->action->uuid, action->uuid);
return false;
} else if (pcmk_is_set(input->action->flags, pe_action_optional)
&& !pcmk_any_flags_set(input->action->flags,
pe_action_print_always|pe_action_dumped)
&& !should_dump_action(input->action)) {
crm_trace("Ignoring %s (%d) input %s (%d): "
"input optional",
action->uuid, action->id,
input->action->uuid, input->action->id);
return false;
}
crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s 0x%.6x",
action->uuid, action->id, action_type_str(input->action->flags),
input->action->uuid, input->action->id,
action_node_str(input->action),
action_runnable_str(input->action->flags),
action_optional_str(input->action->flags), input->type);
return true;
}
bool
pcmk__graph_has_loop(pe_action_t *init_action, pe_action_t *action,
pe_action_wrapper_t *input)
{
bool has_loop = false;
if (pcmk_is_set(input->action->flags, pe_action_tracking)) {
crm_trace("Breaking tracking loop: %s@%s -> %s@%s (0x%.6x)",
input->action->uuid,
input->action->node? input->action->node->details->uname : "",
action->uuid,
action->node? action->node->details->uname : "",
input->type);
return false;
}
// Don't need to check inputs that won't be used
if (!check_dump_input(action, input)) {
return false;
}
if (input->action == init_action) {
crm_debug("Input loop found in %s@%s ->...-> %s@%s",
action->uuid,
action->node? action->node->details->uname : "",
init_action->uuid,
init_action->node? init_action->node->details->uname : "");
return true;
}
pe__set_action_flags(input->action, pe_action_tracking);
crm_trace("Checking inputs of action %s@%s input %s@%s (0x%.6x)"
"for graph loop with %s@%s ",
action->uuid,
action->node? action->node->details->uname : "",
input->action->uuid,
input->action->node? input->action->node->details->uname : "",
input->type,
init_action->uuid,
init_action->node? init_action->node->details->uname : "");
// Recursively check input itself for loops
for (GList *iter = input->action->actions_before;
iter != NULL; iter = iter->next) {
if (pcmk__graph_has_loop(init_action, input->action,
(pe_action_wrapper_t *) iter->data)) {
// Recursive call already logged a debug message
has_loop = true;
goto done;
}
}
done:
pe__clear_action_flags(input->action, pe_action_tracking);
if (!has_loop) {
crm_trace("No input loop found in %s@%s -> %s@%s (0x%.6x)",
input->action->uuid,
input->action->node? input->action->node->details->uname : "",
action->uuid,
action->node? action->node->details->uname : "",
input->type);
}
return has_loop;
}
// Remove duplicate inputs (regardless of flags)
static void
deduplicate_inputs(pe_action_t *action)
{
GList *item = NULL;
GList *next = NULL;
pe_action_wrapper_t *last_input = NULL;
action->actions_before = g_list_sort(action->actions_before,
sort_action_id);
for (item = action->actions_before; item != NULL; item = next) {
pe_action_wrapper_t *input = (pe_action_wrapper_t *) item->data;
next = item->next;
if (last_input && (input->action->id == last_input->action->id)) {
crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
input->action->uuid, input->action->id,
action->uuid, action->id);
/* For the purposes of scheduling, the ordering flags no longer
* matter, but crm_simulate looks at certain ones when creating a
* dot graph. Combining the flags is sufficient for that purpose.
*/
last_input->type |= input->type;
if (input->state == pe_link_dumped) {
last_input->state = pe_link_dumped;
}
free(item->data);
action->actions_before = g_list_delete_link(action->actions_before,
item);
} else {
last_input = input;
input->state = pe_link_not_dumped;
}
}
}
/*!
* \internal
* \brief Add an action to the transition graph XML if appropriate
*
* \param[in] action Action to possibly add
* \param[in] data_set Cluster working set
*
* \note This will de-duplicate the action inputs, meaning that the
* pe_action_wrapper_t:type flags can no longer be relied on to retain
* their original settings. That means this MUST be called after
* pcmk__apply_orderings() is complete, and nothing after this should rely
* on those type flags. (For example, some code looks for type equal to
* some flag rather than whether the flag is set, and some code looks for
* particular combinations of flags -- such code must be done before
* stage8().)
*/
void
graph_element_from_action(pe_action_t *action, pe_working_set_t *data_set)
{
GList *lpc = NULL;
int synapse_priority = 0;
xmlNode *syn = NULL;
xmlNode *set = NULL;
xmlNode *in = NULL;
xmlNode *xml_action = NULL;
pe_action_wrapper_t *input = NULL;
/* If we haven't already, de-duplicate inputs -- even if we won't be dumping
* the action, so that crm_simulate dot graphs don't have duplicates.
*/
if (!pcmk_is_set(action->flags, pe_action_dedup)) {
deduplicate_inputs(action);
pe__set_action_flags(action, pe_action_dedup);
}
if (should_dump_action(action) == FALSE) {
return;
}
pe__set_action_flags(action, pe_action_dumped);
syn = create_xml_node(data_set->graph, "synapse");
set = create_xml_node(syn, "action_set");
in = create_xml_node(syn, "inputs");
crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse);
data_set->num_synapse++;
if (action->rsc != NULL) {
synapse_priority = action->rsc->priority;
}
if (action->priority > synapse_priority) {
synapse_priority = action->priority;
}
if (synapse_priority > 0) {
crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
}
xml_action = action2xml(action, FALSE, data_set);
add_node_nocopy(set, crm_element_name(xml_action), xml_action);
for (lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
input = (pe_action_wrapper_t *) lpc->data;
if (check_dump_input(action, input)) {
xmlNode *input_xml = create_xml_node(in, "trigger");
input->state = pe_link_dumped;
xml_action = action2xml(input->action, TRUE, data_set);
add_node_nocopy(input_xml, crm_element_name(xml_action), xml_action);
}
}
}
diff --git a/lib/pacemaker/pcmk_sched_actions.c b/lib/pacemaker/pcmk_sched_actions.c
new file mode 100644
index 0000000000..27b6b42f88
--- /dev/null
+++ b/lib/pacemaker/pcmk_sched_actions.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2004-2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <glib.h>
+
+#include <pacemaker-internal.h>
+#include "libpacemaker_private.h"
+
+static enum pe_action_flags
+get_action_flags(pe_action_t *action, pe_node_t *node)
+{
+ enum pe_action_flags flags = action->flags;
+
+ if (action->rsc) {
+ flags = action->rsc->cmds->action_flags(action, NULL);
+
+ if (pe_rsc_is_clone(action->rsc) && node) {
+
+ /* We only care about activity on $node */
+ enum pe_action_flags clone_flags = action->rsc->cmds->action_flags(action, node);
+
+ /* Go to great lengths to ensure the correct value for pe_action_runnable...
+ *
+ * If we are a clone, then for _ordering_ constraints, it's only relevant
+ * if we are runnable _anywhere_.
+ *
+ * This only applies to _runnable_ though, and only for ordering constraints.
+ * If this function is ever used during colocation, then we'll need additional logic
+ *
+ * Not very satisfying, but it's logical and appears to work well.
+ */
+ if (!pcmk_is_set(clone_flags, pe_action_runnable)
+ && pcmk_is_set(flags, pe_action_runnable)) {
+
+ pe__set_raw_action_flags(clone_flags, action->rsc->id,
+ pe_action_runnable);
+ }
+ flags = clone_flags;
+ }
+ }
+ return flags;
+}
+
+static char *
+convert_non_atomic_uuid(char *old_uuid, pe_resource_t * rsc, gboolean allow_notify,
+ gboolean free_original)
+{
+ guint interval_ms = 0;
+ char *uuid = NULL;
+ char *rid = NULL;
+ char *raw_task = NULL;
+ int task = no_action;
+
+ CRM_ASSERT(rsc);
+ pe_rsc_trace(rsc, "Processing %s", old_uuid);
+ if (old_uuid == NULL) {
+ return NULL;
+
+ } else if (strstr(old_uuid, "notify") != NULL) {
+ goto done; /* no conversion */
+
+ } else if (rsc->variant < pe_group) {
+ goto done; /* no conversion */
+ }
+
+ CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval_ms));
+ if (interval_ms > 0) {
+ goto done; /* no conversion */
+ }
+
+ task = text2task(raw_task);
+ switch (task) {
+ case stop_rsc:
+ case start_rsc:
+ case action_notify:
+ case action_promote:
+ case action_demote:
+ break;
+ case stopped_rsc:
+ case started_rsc:
+ case action_notified:
+ case action_promoted:
+ case action_demoted:
+ task--;
+ break;
+ case monitor_rsc:
+ case shutdown_crm:
+ case stonith_node:
+ task = no_action;
+ break;
+ default:
+ crm_err("Unknown action: %s", raw_task);
+ task = no_action;
+ break;
+ }
+
+ if (task != no_action) {
+ if (pcmk_is_set(rsc->flags, pe_rsc_notify) && allow_notify) {
+ uuid = pcmk__notify_key(rid, "confirmed-post", task2text(task + 1));
+
+ } else {
+ uuid = pcmk__op_key(rid, task2text(task + 1), 0);
+ }
+ pe_rsc_trace(rsc, "Converted %s -> %s", old_uuid, uuid);
+ }
+
+ done:
+ if (uuid == NULL) {
+ uuid = strdup(old_uuid);
+ }
+
+ if (free_original) {
+ free(old_uuid);
+ }
+
+ free(raw_task);
+ free(rid);
+ return uuid;
+}
+
+static pe_action_t *
+rsc_expand_action(pe_action_t * action)
+{
+ gboolean notify = FALSE;
+ pe_action_t *result = action;
+ pe_resource_t *rsc = action->rsc;
+
+ if (rsc == NULL) {
+ return action;
+ }
+
+ if ((rsc->parent == NULL)
+ || (pe_rsc_is_clone(rsc) && (rsc->parent->variant == pe_container))) {
+ /* Only outermost resources have notification actions.
+ * The exception is those in bundles.
+ */
+ notify = pcmk_is_set(rsc->flags, pe_rsc_notify);
+ }
+
+ if (rsc->variant >= pe_group) {
+ /* Expand 'start' -> 'started' */
+ char *uuid = NULL;
+
+ uuid = convert_non_atomic_uuid(action->uuid, rsc, notify, FALSE);
+ if (uuid) {
+ pe_rsc_trace(rsc, "Converting %s to %s %d", action->uuid, uuid,
+ pcmk_is_set(rsc->flags, pe_rsc_notify));
+ result = find_first_action(rsc->actions, uuid, NULL, NULL);
+ if (result == NULL) {
+ crm_err("Couldn't expand %s to %s in %s", action->uuid, uuid, rsc->id);
+ result = action;
+ }
+ free(uuid);
+ }
+ }
+ return result;
+}
+
+static enum pe_graph_flags
+graph_update_action(pe_action_t * first, pe_action_t * then, pe_node_t * node,
+ enum pe_action_flags first_flags, enum pe_action_flags then_flags,
+ pe_action_wrapper_t *order, pe_working_set_t *data_set)
+{
+ enum pe_graph_flags changed = pe_graph_none;
+ enum pe_ordering type = order->type;
+
+ /* TODO: Do as many of these in parallel as possible */
+
+ if (pcmk_is_set(type, pe_order_implies_then_on_node)) {
+ /* Normally we want the _whole_ 'then' clone to
+ * restart if 'first' is restarted, so then->node is
+ * needed.
+ *
+ * However for unfencing, we want to limit this to
+ * instances on the same node as 'first' (the
+ * unfencing operation), so first->node is supplied.
+ *
+ * Swap the node, from then on we can can treat it
+ * like any other 'pe_order_implies_then'
+ */
+
+ pe__clear_order_flags(type, pe_order_implies_then_on_node);
+ pe__set_order_flags(type, pe_order_implies_then);
+ node = first->node;
+ pe_rsc_trace(then->rsc,
+ "%s then %s: mapped pe_order_implies_then_on_node to "
+ "pe_order_implies_then on %s",
+ first->uuid, then->uuid, node->details->uname);
+ }
+
+ if (type & pe_order_implies_then) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags & pe_action_optional, pe_action_optional,
+ pe_order_implies_then, data_set);
+
+ } else if (!pcmk_is_set(first_flags, pe_action_optional)
+ && pcmk_is_set(then->flags, pe_action_optional)) {
+ pe__clear_action_flags(then, pe_action_optional);
+ pe__set_graph_flags(changed, first, pe_graph_updated_then);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_then",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if ((type & pe_order_restart) && then->rsc) {
+ enum pe_action_flags restart = (pe_action_optional | pe_action_runnable);
+
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, restart,
+ pe_order_restart, data_set);
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_restart",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_implies_first) {
+ if (first->rsc) {
+ changed |= first->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_optional, pe_order_implies_first,
+ data_set);
+
+ } else if (!pcmk_is_set(first_flags, pe_action_optional)
+ && pcmk_is_set(first->flags, pe_action_runnable)) {
+ pe__clear_action_flags(first, pe_action_runnable);
+ pe__set_graph_flags(changed, first, pe_graph_updated_first);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_first",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_promoted_implies_first) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags & pe_action_optional, pe_action_optional,
+ pe_order_promoted_implies_first, data_set);
+ }
+ pe_rsc_trace(then->rsc,
+ "%s then %s: %s after pe_order_promoted_implies_first",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_one_or_more) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_runnable, pe_order_one_or_more,
+ data_set);
+
+ } else if (pcmk_is_set(first_flags, pe_action_runnable)) {
+ // We have another runnable instance of "first"
+ then->runnable_before++;
+
+ /* Mark "then" as runnable if it requires a certain number of
+ * "before" instances to be runnable, and they now are.
+ */
+ if ((then->runnable_before >= then->required_runnable_before)
+ && !pcmk_is_set(then->flags, pe_action_runnable)) {
+
+ pe__set_action_flags(then, pe_action_runnable);
+ pe__set_graph_flags(changed, first, pe_graph_updated_then);
+ }
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_one_or_more",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (then->rsc && pcmk_is_set(type, pe_order_probe)) {
+ if (!pcmk_is_set(first_flags, pe_action_runnable)
+ && (first->rsc->running_on != NULL)) {
+
+ pe_rsc_trace(then->rsc,
+ "%s then %s: ignoring because first is stopping",
+ first->uuid, then->uuid);
+ type = pe_order_none;
+ order->type = pe_order_none;
+
+ } else {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_runnable, pe_order_runnable_left,
+ data_set);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_probe",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_runnable_left) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_runnable, pe_order_runnable_left,
+ data_set);
+
+ } else if (!pcmk_is_set(first_flags, pe_action_runnable)
+ && pcmk_is_set(then->flags, pe_action_runnable)) {
+
+ pe__clear_action_flags(then, pe_action_runnable);
+ pe__set_graph_flags(changed, first, pe_graph_updated_then);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_runnable_left",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_implies_first_migratable) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_optional,
+ pe_order_implies_first_migratable, data_set);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after "
+ "pe_order_implies_first_migratable",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_pseudo_left) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_optional, pe_order_pseudo_left,
+ data_set);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_pseudo_left",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_optional) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_runnable, pe_order_optional, data_set);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_optional",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if (type & pe_order_asymmetrical) {
+ if (then->rsc) {
+ changed |= then->rsc->cmds->update_actions(first, then, node,
+ first_flags, pe_action_runnable, pe_order_asymmetrical,
+ data_set);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_asymmetrical",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ if ((first->flags & pe_action_runnable) && (type & pe_order_implies_then_printed)
+ && (first_flags & pe_action_optional) == 0) {
+ pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
+ then->uuid, first->uuid);
+ pe__set_action_flags(then, pe_action_print_always);
+ // Don't bother marking 'then' as changed just for this
+ }
+
+ if (pcmk_is_set(type, pe_order_implies_first_printed)
+ && !pcmk_is_set(then_flags, pe_action_optional)) {
+
+ pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
+ first->uuid, then->uuid);
+ pe__set_action_flags(first, pe_action_print_always);
+ // Don't bother marking 'first' as changed just for this
+ }
+
+ if ((type & pe_order_implies_then
+ || type & pe_order_implies_first
+ || type & pe_order_restart)
+ && first->rsc
+ && pcmk__str_eq(first->task, RSC_STOP, pcmk__str_casei)
+ && !pcmk_is_set(first->rsc->flags, pe_rsc_managed)
+ && pcmk_is_set(first->rsc->flags, pe_rsc_block)
+ && !pcmk_is_set(first->flags, pe_action_runnable)) {
+
+ if (pcmk_is_set(then->flags, pe_action_runnable)) {
+ pe__clear_action_flags(then, pe_action_runnable);
+ pe__set_graph_flags(changed, first, pe_graph_updated_then);
+ }
+ pe_rsc_trace(then->rsc, "%s then %s: %s after checking whether first "
+ "is blocked, unmanaged, unrunnable stop",
+ first->uuid, then->uuid,
+ (changed? "changed" : "unchanged"));
+ }
+
+ return changed;
+}
+
+// Convenience macros for logging action properties
+
+#define action_type_str(flags) \
+ (pcmk_is_set((flags), pe_action_pseudo)? "pseudo-action" : "action")
+
+#define action_optional_str(flags) \
+ (pcmk_is_set((flags), pe_action_optional)? "optional" : "required")
+
+#define action_runnable_str(flags) \
+ (pcmk_is_set((flags), pe_action_runnable)? "runnable" : "unrunnable")
+
+#define action_node_str(a) \
+ (((a)->node == NULL)? "no node" : (a)->node->details->uname)
+
+gboolean
+update_action(pe_action_t *then, pe_working_set_t *data_set)
+{
+ GList *lpc = NULL;
+ enum pe_graph_flags changed = pe_graph_none;
+ int last_flags = then->flags;
+
+ pe_rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
+ action_type_str(then->flags), then->uuid,
+ action_optional_str(then->flags),
+ action_runnable_str(then->flags), action_node_str(then));
+
+ if (pcmk_is_set(then->flags, pe_action_requires_any)) {
+ /* initialize current known runnable before actions to 0
+ * from here as graph_update_action is called for each of
+ * then's before actions, this number will increment as
+ * runnable 'first' actions are encountered */
+ then->runnable_before = 0;
+
+ /* for backwards compatibility with previous options that use
+ * the 'requires_any' flag, initialize required to 1 if it is
+ * not set. */
+ if (then->required_runnable_before == 0) {
+ then->required_runnable_before = 1;
+ }
+ pe__clear_action_flags(then, pe_action_runnable);
+ /* We are relying on the pe_order_one_or_more clause of
+ * graph_update_action(), called as part of the:
+ *
+ * 'if (first == other->action)'
+ *
+ * block below, to set this back if appropriate
+ */
+ }
+
+ for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
+ pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
+ pe_action_t *first = other->action;
+
+ pe_node_t *then_node = then->node;
+ pe_node_t *first_node = first->node;
+
+ enum pe_action_flags then_flags = 0;
+ enum pe_action_flags first_flags = 0;
+
+ if (first->rsc && first->rsc->variant == pe_group && pcmk__str_eq(first->task, RSC_START, pcmk__str_casei)) {
+ first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
+ if (first_node) {
+ pe_rsc_trace(first->rsc, "Found node %s for 'first' %s",
+ first_node->details->uname, first->uuid);
+ }
+ }
+
+ if (then->rsc && then->rsc->variant == pe_group && pcmk__str_eq(then->task, RSC_START, pcmk__str_casei)) {
+ then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
+ if (then_node) {
+ pe_rsc_trace(then->rsc, "Found node %s for 'then' %s",
+ then_node->details->uname, then->uuid);
+ }
+ }
+ /* Disable constraint if it only applies when on same node, but isn't */
+ if (pcmk_is_set(other->type, pe_order_same_node)
+ && (first_node != NULL) && (then_node != NULL)
+ && (first_node->details != then_node->details)) {
+
+ pe_rsc_trace(then->rsc,
+ "Disabled ordering %s on %s then %s on %s: not same node",
+ other->action->uuid, first_node->details->uname,
+ then->uuid, then_node->details->uname);
+ other->type = pe_order_none;
+ continue;
+ }
+
+ pe__clear_graph_flags(changed, then, pe_graph_updated_first);
+
+ if (first->rsc && pcmk_is_set(other->type, pe_order_then_cancels_first)
+ && !pcmk_is_set(then->flags, pe_action_optional)) {
+
+ /* 'then' is required, so we must abandon 'first'
+ * (e.g. a required stop cancels any agent reload).
+ */
+ pe__set_action_flags(other->action, pe_action_optional);
+ if (!strcmp(first->task, CRMD_ACTION_RELOAD_AGENT)) {
+ pe__clear_resource_flags(first->rsc, pe_rsc_reload);
+ }
+ }
+
+ if (first->rsc && then->rsc && (first->rsc != then->rsc)
+ && (is_parent(then->rsc, first->rsc) == FALSE)) {
+ first = rsc_expand_action(first);
+ }
+ if (first != other->action) {
+ pe_rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
+ then->uuid, first->uuid, other->action->uuid);
+ }
+
+ first_flags = get_action_flags(first, then_node);
+ then_flags = get_action_flags(then, first_node);
+
+ pe_rsc_trace(then->rsc,
+ "%s then %s: type=0x%.6x filter=0x%.6x "
+ "(%s %s %s on %s 0x%.6x then 0x%.6x)",
+ first->uuid, then->uuid, other->type, first_flags,
+ action_optional_str(first_flags),
+ action_runnable_str(first_flags),
+ action_type_str(first_flags), action_node_str(first),
+ first->flags, then->flags);
+
+ if (first == other->action) {
+ /*
+ * 'first' was not expanded (e.g. from 'start' to 'running'), which could mean it:
+ * - has no associated resource,
+ * - was a primitive,
+ * - was pre-expanded (e.g. 'running' instead of 'start')
+ *
+ * The third argument here to graph_update_action() is a node which is used under two conditions:
+ * - Interleaving, in which case first->node and
+ * then->node are equal (and NULL)
+ * - If 'then' is a clone, to limit the scope of the
+ * constraint to instances on the supplied node
+ *
+ */
+ pe_node_t *node = then->node;
+ changed |= graph_update_action(first, then, node, first_flags,
+ then_flags, other, data_set);
+
+ /* 'first' was for a complex resource (clone, group, etc),
+ * create a new dependency if necessary
+ */
+ } else if (order_actions(first, then, other->type)) {
+ /* This was the first time 'first' and 'then' were associated,
+ * start again to get the new actions_before list
+ */
+ pe__set_graph_flags(changed, then,
+ pe_graph_updated_then|pe_graph_disable);
+ }
+
+ if (changed & pe_graph_disable) {
+ pe_rsc_trace(then->rsc,
+ "Disabled ordering %s then %s in favor of %s then %s",
+ other->action->uuid, then->uuid, first->uuid,
+ then->uuid);
+ pe__clear_graph_flags(changed, then, pe_graph_disable);
+ other->type = pe_order_none;
+ }
+
+ if (changed & pe_graph_updated_first) {
+ GList *lpc2 = NULL;
+
+ crm_trace("Re-processing %s and its 'after' actions since it changed",
+ first->uuid);
+ for (lpc2 = first->actions_after; lpc2 != NULL; lpc2 = lpc2->next) {
+ pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc2->data;
+
+ update_action(other->action, data_set);
+ }
+ update_action(first, data_set);
+ }
+ }
+
+ if (pcmk_is_set(then->flags, pe_action_requires_any)) {
+ if (last_flags != then->flags) {
+ pe__set_graph_flags(changed, then, pe_graph_updated_then);
+ } else {
+ pe__clear_graph_flags(changed, then, pe_graph_updated_then);
+ }
+ }
+
+ if (changed & pe_graph_updated_then) {
+ crm_trace("Re-processing %s and its 'after' actions since it changed",
+ then->uuid);
+ if (pcmk_is_set(last_flags, pe_action_runnable)
+ && !pcmk_is_set(then->flags, pe_action_runnable)) {
+ pcmk__block_colocated_starts(then, data_set);
+ }
+ update_action(then, data_set);
+ for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
+ pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
+
+ update_action(other->action, data_set);
+ }
+ }
+
+ return FALSE;
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 5:59 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002422
Default Alt Text
(85 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment