Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/lib/pacemaker/pcmk_sched_actions.c b/lib/pacemaker/pcmk_sched_actions.c
index 095bb4e2e4..ba6f65f93f 100644
--- a/lib/pacemaker/pcmk_sched_actions.c
+++ b/lib/pacemaker/pcmk_sched_actions.c
@@ -1,851 +1,1072 @@
/*
* 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 <crm/lrmd_internal.h>
#include <pacemaker-internal.h>
#include "libpacemaker_private.h"
/*!
* \internal
* \brief Get the action flags relevant to ordering constraints
*
* \param[in] action Action to check
* \param[in] node Node that *other* action in the ordering is on
* (used only for clone resource actions)
*
* \return Action flags that should be used for orderings
*/
static enum pe_action_flags
action_flags_for_ordering(pe_action_t *action, pe_node_t *node)
{
bool runnable = false;
enum pe_action_flags flags;
// For non-resource actions, return the action flags
if (action->rsc == NULL) {
return action->flags;
}
/* For non-clone resources, or a clone action not assigned to a node,
* return the flags as determined by the resource method without a node
* specified.
*/
flags = action->rsc->cmds->action_flags(action, NULL);
if ((node == NULL) || !pe_rsc_is_clone(action->rsc)) {
return flags;
}
/* Otherwise (i.e., for clone resource actions on a specific node), first
* remember whether the non-node-specific action is runnable.
*/
runnable = pcmk_is_set(flags, pe_action_runnable);
// Then recheck the resource method with the node
flags = action->rsc->cmds->action_flags(action, node);
/* For clones in ordering constraints, the node-specific "runnable" doesn't
* matter, just the non-node-specific setting (i.e., is the action runnable
* anywhere).
*
* This applies only to runnable, and only for ordering constraints. This
* function shouldn't be used for other types of constraints without
* changes. Not very satisfying, but it's logical and appears to work well.
*/
if (runnable && !pcmk_is_set(flags, pe_action_runnable)) {
pe__set_raw_action_flags(flags, action->rsc->id,
pe_action_runnable);
}
return flags;
}
/*!
* \internal
* \brief Get action UUID that should be used with a resource ordering
*
* When an action is ordered relative to an action for a collective resource
* (clone, group, or bundle), it actually needs to be ordered after all
* instances of the collective have completed the relevant action (for example,
* given "start CLONE then start RSC", RSC must wait until all instances of
* CLONE have started). Given the UUID and resource of the first action in an
* ordering, this returns the UUID of the action that should actually be used
* for ordering (for example, "CLONE_started_0" instead of "CLONE_start_0").
*
* \param[in] first_uuid UUID of first action in ordering
* \param[in] first_rsc Resource of first action in ordering
*
* \return Newly allocated copy of UUID to use with ordering
* \note It is the caller's responsibility to free the return value.
*/
static char *
action_uuid_for_ordering(const char *first_uuid, pe_resource_t *first_rsc)
{
guint interval_ms = 0;
char *uuid = NULL;
char *rid = NULL;
char *first_task_str = NULL;
enum action_tasks first_task = no_action;
enum action_tasks remapped_task = no_action;
// Only non-notify actions for collective resources need remapping
if ((strstr(first_uuid, "notify") != NULL)
|| (first_rsc->variant < pe_group)) {
goto done;
}
// Only non-recurring actions need remapping
CRM_ASSERT(parse_op_key(first_uuid, &rid, &first_task_str, &interval_ms));
if (interval_ms > 0) {
goto done;
}
first_task = text2task(first_task_str);
switch (first_task) {
case stop_rsc:
case start_rsc:
case action_notify:
case action_promote:
case action_demote:
remapped_task = first_task + 1;
break;
case stopped_rsc:
case started_rsc:
case action_notified:
case action_promoted:
case action_demoted:
remapped_task = first_task;
break;
case monitor_rsc:
case shutdown_crm:
case stonith_node:
break;
default:
crm_err("Unknown action '%s' in ordering", first_task_str);
break;
}
if (remapped_task != no_action) {
/* If a (clone) resource has notifications enabled, we want to order
* relative to when all notifications have been sent for the remapped
* task. Only outermost resources or those in bundles have
* notifications.
*/
if (pcmk_is_set(first_rsc->flags, pe_rsc_notify)
&& ((first_rsc->parent == NULL)
|| (pe_rsc_is_clone(first_rsc)
&& (first_rsc->parent->variant == pe_container)))) {
uuid = pcmk__notify_key(rid, "confirmed-post",
task2text(remapped_task));
} else {
uuid = pcmk__op_key(rid, task2text(remapped_task), 0);
}
pe_rsc_trace(first_rsc,
"Remapped action UUID %s to %s for ordering purposes",
first_uuid, uuid);
}
done:
if (uuid == NULL) {
uuid = strdup(first_uuid);
CRM_ASSERT(uuid != NULL);
}
free(first_task_str);
free(rid);
return uuid;
}
/*!
* \internal
* \brief Get actual action that should be used with an ordering
*
* When an action is ordered relative to an action for a collective resource
* (clone, group, or bundle), it actually needs to be ordered after all
* instances of the collective have completed the relevant action (for example,
* given "start CLONE then start RSC", RSC must wait until all instances of
* CLONE have started). Given the first action in an ordering, this returns the
* the action that should actually be used for ordering (for example, the
* started action instead of the start action).
*
* \param[in] action First action in an ordering
*
* \return Actual action that should be used for the ordering
*/
static pe_action_t *
action_for_ordering(pe_action_t *action)
{
pe_action_t *result = action;
pe_resource_t *rsc = action->rsc;
if ((rsc != NULL) && (rsc->variant >= pe_group) && (action->uuid != NULL)) {
char *uuid = action_uuid_for_ordering(action->uuid, rsc);
result = find_first_action(rsc->actions, uuid, NULL, NULL);
if (result == NULL) {
crm_warn("Not remapping %s to %s because %s does not have "
"remapped action", action->uuid, uuid, rsc->id);
result = action;
}
free(uuid);
}
return result;
}
/*!
* \internal
* \brief Update flags for ordering's actions appropriately for ordering's flags
*
* \param[in] first First action in an ordering
* \param[in] then Then action in an ordering
* \param[in] first_flags Action flags for \p first for ordering purposes
* \param[in] then_flags Action flags for \p then for ordering purposes
* \param[in] order Action wrapper for \p first in ordering
* \param[in] data_set Cluster working set
*
* \return Mask of pe_graph_updated_first and/or pe_graph_updated_then
*/
static enum pe_graph_flags
update_action_for_ordering_flags(pe_action_t *first, pe_action_t *then,
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;
/* The node will only be used for clones. If interleaved, node will be NULL,
* otherwise the ordering scope will be limited to the node. Normally, the
* whole 'then' clone should restart if 'first' is restarted, so then->node
* is needed.
*/
pe_node_t *node = then->node;
if (pcmk_is_set(order->type, pe_order_implies_then_on_node)) {
/* For unfencing, only instances of 'then' on the same node as 'first'
* (the unfencing operation) should restart, so reset node to
* first->node, at which point this case is handled like a normal
* pe_order_implies_then.
*/
pe__clear_order_flags(order->type, pe_order_implies_then_on_node);
pe__set_order_flags(order->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 (pcmk_is_set(order->type, pe_order_implies_then)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_restart) && (then->rsc != NULL)) {
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 (pcmk_is_set(order->type, pe_order_implies_first)) {
if (first->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_promoted_implies_first)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_one_or_more)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_probe) && (then->rsc != NULL)) {
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);
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 (pcmk_is_set(order->type, pe_order_runnable_left)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_implies_first_migratable)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_pseudo_left)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_optional)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(order->type, pe_order_asymmetrical)) {
if (then->rsc != NULL) {
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 (pcmk_is_set(first->flags, pe_action_runnable)
&& pcmk_is_set(order->type, pe_order_implies_then_printed)
&& !pcmk_is_set(first_flags, pe_action_optional)) {
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(order->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 (pcmk_any_flags_set(order->type, pe_order_implies_then
|pe_order_implies_first
|pe_order_restart)
&& (first->rsc != NULL)
&& !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)
&& pcmk__str_eq(first->task, RSC_STOP, pcmk__str_casei)) {
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)
/*!
* \internal
* \brief Update an action's flags for all orderings where it is "then"
*
* \param[in] then Action to update
* \param[in] data_set Cluster working set
*/
void
pcmk__update_action_for_orderings(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. As
* update_action_for_ordering_flags() is called for each of then's
* before actions, this number will increment as runnable 'first'
* actions are encountered.
*/
then->runnable_before = 0;
if (then->required_runnable_before == 0) {
/* @COMPAT This ordering constraint uses the deprecated
* "require-all=false" attribute. Treat it like "clone-min=1".
*/
then->required_runnable_before = 1;
}
/* The pe_order_one_or_more clause of update_action_for_ordering_flags()
* (called below) will reset runnable if appropriate.
*/
pe__clear_action_flags(then, pe_action_runnable);
}
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;
if ((first->rsc != NULL)
&& (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 != NULL) {
pe_rsc_trace(first->rsc, "Found node %s for 'first' %s",
first_node->details->uname, first->uuid);
}
}
if ((then->rsc != NULL)
&& (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 != NULL) {
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 != NULL)
&& 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 != NULL) && (then->rsc != NULL)
&& (first->rsc != then->rsc) && !is_parent(then->rsc, first->rsc)) {
first = action_for_ordering(first);
}
if (first != other->action) {
pe_rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
then->uuid, first->uuid, other->action->uuid);
}
pe_rsc_trace(then->rsc,
"%s (0x%.6x) then %s (0x%.6x): type=0x%.6x node=%s",
first->uuid, first->flags, then->uuid, then->flags,
other->type, action_node_str(first));
if (first == other->action) {
/* 'first' was not remapped (e.g. from 'start' to 'running'), which
* could mean it is a non-resource action, a primitive resource
* action, or already expanded.
*/
enum pe_action_flags first_flags, then_flags;
first_flags = action_flags_for_ordering(first, then_node);
then_flags = action_flags_for_ordering(then, first_node);
changed |= update_action_for_ordering_flags(first, then,
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 (pcmk_is_set(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 (pcmk_is_set(changed, pe_graph_updated_first)) {
crm_trace("Re-processing %s and its 'after' actions "
"because it changed", first->uuid);
for (GList *lpc2 = first->actions_after; lpc2 != NULL;
lpc2 = lpc2->next) {
pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc2->data;
pcmk__update_action_for_orderings(other->action, data_set);
}
pcmk__update_action_for_orderings(first, data_set);
}
}
if (pcmk_is_set(then->flags, pe_action_requires_any)) {
if (last_flags == then->flags) {
pe__clear_graph_flags(changed, then, pe_graph_updated_then);
} else {
pe__set_graph_flags(changed, then, pe_graph_updated_then);
}
}
if (pcmk_is_set(changed, pe_graph_updated_then)) {
crm_trace("Re-processing %s and its 'after' actions because 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);
}
pcmk__update_action_for_orderings(then, data_set);
for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
pcmk__update_action_for_orderings(other->action, data_set);
}
}
}
/*!
* \internal
* \brief Trace-log an action (optionally with its dependent actions)
*
* \param[in] pre_text If not NULL, prefix the log with this plus ": "
* \param[in] action Action to log
* \param[in] details If true, recursively log dependent actions
*/
void
pcmk__log_action(const char *pre_text, pe_action_t *action, bool details)
{
const char *node_uname = NULL;
const char *node_uuid = NULL;
const char *desc = NULL;
CRM_CHECK(action != NULL, return);
if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
if (action->node != NULL) {
node_uname = action->node->details->uname;
node_uuid = action->node->details->id;
} else {
node_uname = "<none>";
}
}
switch (text2task(action->task)) {
case stonith_node:
case shutdown_crm:
if (pcmk_is_set(action->flags, pe_action_pseudo)) {
desc = "Pseudo ";
} else if (pcmk_is_set(action->flags, pe_action_optional)) {
desc = "Optional ";
} else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
desc = "!!Non-Startable!! ";
} else if (pcmk_is_set(action->flags, pe_action_processed)) {
desc = "";
} else {
desc = "(Provisional) ";
}
crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
((pre_text == NULL)? "" : pre_text),
((pre_text == NULL)? "" : ": "),
desc, action->id, action->uuid,
(node_uname? "\ton " : ""), (node_uname? node_uname : ""),
(node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
(node_uuid? ")" : ""));
break;
default:
if (pcmk_is_set(action->flags, pe_action_optional)) {
desc = "Optional ";
} else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
desc = "Pseudo ";
} else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
desc = "!!Non-Startable!! ";
} else if (pcmk_is_set(action->flags, pe_action_processed)) {
desc = "";
} else {
desc = "(Provisional) ";
}
crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
((pre_text == NULL)? "" : pre_text),
((pre_text == NULL)? "" : ": "),
desc, action->id, action->uuid,
(action->rsc? action->rsc->id : "<none>"),
(node_uname? "\ton " : ""), (node_uname? node_uname : ""),
(node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
(node_uuid? ")" : ""));
break;
}
if (details) {
GList *iter = NULL;
crm_trace("\t\t====== Preceding Actions");
for (iter = action->actions_before; iter != NULL; iter = iter->next) {
pe_action_wrapper_t *other = (pe_action_wrapper_t *) iter->data;
pcmk__log_action("\t\t", other->action, false);
}
crm_trace("\t\t====== Subsequent Actions");
for (iter = action->actions_after; iter != NULL; iter = iter->next) {
pe_action_wrapper_t *other = (pe_action_wrapper_t *) iter->data;
pcmk__log_action("\t\t", other->action, false);
}
crm_trace("\t\t====== End");
} else {
crm_trace("\t\t(before=%d, after=%d)",
g_list_length(action->actions_before),
g_list_length(action->actions_after));
}
}
/*!
* \internal
* \brief Create a new pseudo-action for a resource
*
* \param[in] rsc Resource to create action for
* \param[in] task Action name
* \param[in] optional Whether action should be considered optional
* \param[in] runnable Whethe action should be considered runnable
*
* \return New action object corresponding to arguments
*/
pe_action_t *
pcmk__new_rsc_pseudo_action(pe_resource_t *rsc, const char *task,
bool optional, bool runnable)
{
pe_action_t *action = NULL;
CRM_ASSERT((rsc != NULL) && (task != NULL));
action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
optional, TRUE, rsc->cluster);
pe__set_action_flags(action, pe_action_pseudo);
if (runnable) {
pe__set_action_flags(action, pe_action_runnable);
}
return action;
}
/*!
* \internal
* \brief Create an executor cancel action
*
* \param[in] rsc Resource of action to cancel
* \param[in] task Name of action to cancel
* \param[in] interval_ms Interval of action to cancel
* \param[in] node Node of action to cancel
* \param[in] data_set Working set of cluster
*
* \return Created op
*/
pe_action_t *
pcmk__new_cancel_action(pe_resource_t *rsc, const char *task, guint interval_ms,
pe_node_t *node)
{
pe_action_t *cancel_op = NULL;
char *key = NULL;
char *interval_ms_s = NULL;
CRM_ASSERT((rsc != NULL) && (task != NULL) && (node != NULL));
// @TODO dangerous if possible to schedule another action with this key
key = pcmk__op_key(rsc->id, task, interval_ms);
cancel_op = custom_action(rsc, key, RSC_CANCEL, node, FALSE, TRUE,
rsc->cluster);
free(cancel_op->task);
cancel_op->task = strdup(RSC_CANCEL);
free(cancel_op->cancel_task);
cancel_op->cancel_task = strdup(task);
interval_ms_s = crm_strdup_printf("%u", interval_ms);
add_hash_param(cancel_op->meta, XML_LRM_ATTR_TASK, task);
add_hash_param(cancel_op->meta, XML_LRM_ATTR_INTERVAL_MS, interval_ms_s);
free(interval_ms_s);
return cancel_op;
}
/*!
* \internal
* \brief Create a new shutdown action for a node
*
* \param[in] node Node being shut down
* \param[in] data_set Working set of cluster
*
* \return Newly created shutdown action for \p node
*/
pe_action_t *
pcmk__new_shutdown_action(pe_node_t *node, pe_working_set_t *data_set)
{
char *shutdown_id = NULL;
pe_action_t *shutdown_op = NULL;
CRM_ASSERT((node != NULL) && (data_set != NULL));
shutdown_id = crm_strdup_printf("%s-%s", CRM_OP_SHUTDOWN,
node->details->uname);
shutdown_op = custom_action(NULL, shutdown_id, CRM_OP_SHUTDOWN, node, FALSE,
TRUE, data_set);
pcmk__order_stops_before_shutdown(node, shutdown_op, data_set);
add_hash_param(shutdown_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
return shutdown_op;
}
+
+static char *
+generate_transition_magic(const char *transition_key, int op_status, int op_rc)
+{
+ CRM_CHECK(transition_key != NULL, return NULL);
+ return crm_strdup_printf("%d:%d;%s", op_status, op_rc, transition_key);
+}
+
+static void
+append_digest(lrmd_event_data_t *op, xmlNode *update, const char *version,
+ const char *magic, int level)
+{
+ /* this will enable us to later determine that the
+ * resource's parameters have changed and we should force
+ * a restart
+ */
+ char *digest = NULL;
+ xmlNode *args_xml = NULL;
+
+ if (op->params == NULL) {
+ return;
+ }
+
+ args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
+ g_hash_table_foreach(op->params, hash2field, args_xml);
+ pcmk__filter_op_for_digest(args_xml);
+ digest = calculate_operation_digest(args_xml, version);
+
+#if 0
+ if (level < get_crm_log_level()
+ && op->interval_ms == 0 && pcmk__str_eq(op->op_type, CRMD_ACTION_START, pcmk__str_none)) {
+ char *digest_source = dump_xml_unformatted(args_xml);
+
+ do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s",
+ digest, ID(update), magic, digest_source);
+ free(digest_source);
+ }
+#endif
+ crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
+
+ free_xml(args_xml);
+ free(digest);
+}
+
+#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+
+/*!
+ * \internal
+ * \brief Create XML for resource operation history update
+ *
+ * \param[in,out] parent Parent XML node to add to
+ * \param[in,out] op Operation event data
+ * \param[in] caller_version DC feature set
+ * \param[in] target_rc Expected result of operation
+ * \param[in] node Name of node on which operation was performed
+ * \param[in] origin Arbitrary description of update source
+ * \param[in] level A log message will be logged at this level
+ *
+ * \return Newly created XML node for history update
+ */
+xmlNode *
+pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op,
+ const char *caller_version, int target_rc,
+ const char *node, const char *origin, int level)
+{
+ char *key = NULL;
+ char *magic = NULL;
+ char *op_id = NULL;
+ char *op_id_additional = NULL;
+ char *local_user_data = NULL;
+ const char *exit_reason = NULL;
+
+ xmlNode *xml_op = NULL;
+ const char *task = NULL;
+
+ CRM_CHECK(op != NULL, return NULL);
+ do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%u)",
+ origin, op->rsc_id, op->op_type,
+ pcmk_exec_status_str(op->op_status), op->interval_ms);
+
+ crm_trace("DC version: %s", caller_version);
+
+ task = op->op_type;
+
+ /* Record a successful agent reload as a start, and a failed one as a
+ * monitor, to make life easier for the scheduler when determining the
+ * current state.
+ *
+ * @COMPAT We should check "reload" here only if the operation was for a
+ * pre-OCF-1.1 resource agent, but we don't know that here, and we should
+ * only ever get results for actions scheduled by us, so we can reasonably
+ * assume any "reload" is actually a pre-1.1 agent reload.
+ */
+ if (pcmk__str_any_of(task, CRMD_ACTION_RELOAD, CRMD_ACTION_RELOAD_AGENT,
+ NULL)) {
+ if (op->op_status == PCMK_EXEC_DONE) {
+ task = CRMD_ACTION_START;
+ } else {
+ task = CRMD_ACTION_STATUS;
+ }
+ }
+
+ key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
+ if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_none)) {
+ const char *n_type = crm_meta_value(op->params, "notify_type");
+ const char *n_task = crm_meta_value(op->params, "notify_operation");
+
+ CRM_LOG_ASSERT(n_type != NULL);
+ CRM_LOG_ASSERT(n_task != NULL);
+ op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
+
+ if (op->op_status != PCMK_EXEC_PENDING) {
+ /* Ignore notify errors.
+ *
+ * @TODO It might be better to keep the correct result here, and
+ * ignore it in process_graph_event().
+ */
+ lrmd__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+ }
+
+ } else if (did_rsc_op_fail(op, target_rc)) {
+ op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
+ if (op->interval_ms == 0) {
+ // Ensure 'last' gets updated, in case record-pending is true
+ op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
+ }
+ exit_reason = op->exit_reason;
+
+ } else if (op->interval_ms > 0) {
+ op_id = strdup(key);
+
+ } else {
+ op_id = pcmk__op_key(op->rsc_id, "last", 0);
+ }
+
+ again:
+ xml_op = pcmk__xe_match(parent, XML_LRM_TAG_RSC_OP, XML_ATTR_ID, op_id);
+ if (xml_op == NULL) {
+ xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
+ }
+
+ if (op->user_data == NULL) {
+ crm_debug("Generating fake transition key for: " PCMK__OP_FMT
+ " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
+ op->call_id, origin);
+ local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
+ FAKE_TE_ID);
+ op->user_data = local_user_data;
+ }
+
+ if(magic == NULL) {
+ magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
+ }
+
+ crm_xml_add(xml_op, XML_ATTR_ID, op_id);
+ crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
+ crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
+ crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
+ crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
+ crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
+ crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
+ crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason == NULL ? "" : exit_reason);
+ crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
+
+ crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
+ crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
+ crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
+ crm_xml_add_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, op->interval_ms);
+
+ if (compare_version("2.1", caller_version) <= 0) {
+ if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
+ crm_trace("Timing data (" PCMK__OP_FMT
+ "): last=%u change=%u exec=%u queue=%u",
+ op->rsc_id, op->op_type, op->interval_ms,
+ op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
+
+ if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
+ // Recurring ops may have changed rc after initial run
+ crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE,
+ (long long) op->t_rcchange);
+ } else {
+ crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE,
+ (long long) op->t_run);
+ }
+
+ crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time);
+ crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time);
+ }
+ }
+
+ if (pcmk__str_any_of(op->op_type, CRMD_ACTION_MIGRATE, CRMD_ACTION_MIGRATED, NULL)) {
+ /*
+ * Record migrate_source and migrate_target always for migrate ops.
+ */
+ const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
+
+ crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
+
+ name = XML_LRM_ATTR_MIGRATE_TARGET;
+ crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
+ }
+
+ append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
+
+ if (op_id_additional) {
+ free(op_id);
+ op_id = op_id_additional;
+ op_id_additional = NULL;
+ goto again;
+ }
+
+ if (local_user_data) {
+ free(local_user_data);
+ op->user_data = NULL;
+ }
+ free(magic);
+ free(op_id);
+ free(key);
+ return xml_op;
+}
diff --git a/lib/pacemaker/pcmk_sched_utils.c b/lib/pacemaker/pcmk_sched_utils.c
index 2839a9ce6b..b2134f991c 100644
--- a/lib/pacemaker/pcmk_sched_utils.c
+++ b/lib/pacemaker/pcmk_sched_utils.c
@@ -1,445 +1,224 @@
/*
* 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 <crm/msg_xml.h>
#include <crm/lrmd.h> // lrmd_event_data_t
#include <crm/common/xml_internal.h>
-#include <crm/lrmd_internal.h>
#include <pacemaker-internal.h>
#include <pacemaker.h>
#include "libpacemaker_private.h"
gboolean
can_run_resources(const pe_node_t * node)
{
if (node == NULL) {
return FALSE;
}
#if 0
if (node->weight < 0) {
return FALSE;
}
#endif
if (node->details->online == FALSE
|| node->details->shutdown || node->details->unclean
|| node->details->standby || node->details->maintenance) {
crm_trace("%s: online=%d, unclean=%d, standby=%d, maintenance=%d",
node->details->uname, node->details->online,
node->details->unclean, node->details->standby, node->details->maintenance);
return FALSE;
}
return TRUE;
}
/*!
* \internal
* \brief Copy a hash table of node objects
*
* \param[in] nodes Hash table to copy
*
* \return New copy of nodes (or NULL if nodes is NULL)
*/
GHashTable *
pcmk__copy_node_table(GHashTable *nodes)
{
GHashTable *new_table = NULL;
GHashTableIter iter;
pe_node_t *node = NULL;
if (nodes == NULL) {
return NULL;
}
new_table = pcmk__strkey_table(NULL, free);
g_hash_table_iter_init(&iter, nodes);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
pe_node_t *new_node = pe__copy_node(node);
g_hash_table_insert(new_table, (gpointer) new_node->details->id,
new_node);
}
return new_table;
}
/*!
* \internal
* \brief Copy a list of node objects
*
* \param[in] list List to copy
* \param[in] reset Set copies' scores to 0
*
* \return New list of shallow copies of nodes in original list
*/
GList *
pcmk__copy_node_list(const GList *list, bool reset)
{
GList *result = NULL;
for (const GList *gIter = list; gIter != NULL; gIter = gIter->next) {
pe_node_t *new_node = NULL;
pe_node_t *this_node = (pe_node_t *) gIter->data;
new_node = pe__copy_node(this_node);
if (reset) {
new_node->weight = 0;
}
result = g_list_prepend(result, new_node);
}
return result;
}
struct node_weight_s {
pe_node_t *active;
pe_working_set_t *data_set;
};
/* return -1 if 'a' is more preferred
* return 1 if 'b' is more preferred
*/
static gint
sort_node_weight(gconstpointer a, gconstpointer b, gpointer data)
{
const pe_node_t *node1 = (const pe_node_t *)a;
const pe_node_t *node2 = (const pe_node_t *)b;
struct node_weight_s *nw = data;
int node1_weight = 0;
int node2_weight = 0;
int result = 0;
if (a == NULL) {
return 1;
}
if (b == NULL) {
return -1;
}
node1_weight = node1->weight;
node2_weight = node2->weight;
if (can_run_resources(node1) == FALSE) {
node1_weight = -INFINITY;
}
if (can_run_resources(node2) == FALSE) {
node2_weight = -INFINITY;
}
if (node1_weight > node2_weight) {
crm_trace("%s (%d) > %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
return -1;
}
if (node1_weight < node2_weight) {
crm_trace("%s (%d) < %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
return 1;
}
crm_trace("%s (%d) == %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
if (pcmk__str_eq(nw->data_set->placement_strategy, "minimal", pcmk__str_casei)) {
goto equal;
}
if (pcmk__str_eq(nw->data_set->placement_strategy, "balanced", pcmk__str_casei)) {
result = compare_capacity(node1, node2);
if (result < 0) {
crm_trace("%s > %s : capacity (%d)",
node1->details->uname, node2->details->uname, result);
return -1;
} else if (result > 0) {
crm_trace("%s < %s : capacity (%d)",
node1->details->uname, node2->details->uname, result);
return 1;
}
}
/* now try to balance resources across the cluster */
if (node1->details->num_resources < node2->details->num_resources) {
crm_trace("%s (%d) > %s (%d) : resources",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return -1;
} else if (node1->details->num_resources > node2->details->num_resources) {
crm_trace("%s (%d) < %s (%d) : resources",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return 1;
}
if (nw->active && nw->active->details == node1->details) {
crm_trace("%s (%d) > %s (%d) : active",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return -1;
} else if (nw->active && nw->active->details == node2->details) {
crm_trace("%s (%d) < %s (%d) : active",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return 1;
}
equal:
crm_trace("%s = %s", node1->details->uname, node2->details->uname);
return strcmp(node1->details->uname, node2->details->uname);
}
GList *
sort_nodes_by_weight(GList *nodes, pe_node_t *active_node,
pe_working_set_t *data_set)
{
struct node_weight_s nw = { active_node, data_set };
return g_list_sort_with_data(nodes, sort_node_weight, &nw);
}
gboolean
can_run_any(GHashTable * nodes)
{
GHashTableIter iter;
pe_node_t *node = NULL;
if (nodes == NULL) {
return FALSE;
}
g_hash_table_iter_init(&iter, nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
if (can_run_resources(node) && node->weight >= 0) {
return TRUE;
}
}
return FALSE;
}
-
-static char *
-generate_transition_magic(const char *transition_key, int op_status, int op_rc)
-{
- CRM_CHECK(transition_key != NULL, return NULL);
- return crm_strdup_printf("%d:%d;%s", op_status, op_rc, transition_key);
-}
-
-static void
-append_digest(lrmd_event_data_t *op, xmlNode *update, const char *version,
- const char *magic, int level)
-{
- /* this will enable us to later determine that the
- * resource's parameters have changed and we should force
- * a restart
- */
- char *digest = NULL;
- xmlNode *args_xml = NULL;
-
- if (op->params == NULL) {
- return;
- }
-
- args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
- g_hash_table_foreach(op->params, hash2field, args_xml);
- pcmk__filter_op_for_digest(args_xml);
- digest = calculate_operation_digest(args_xml, version);
-
-#if 0
- if (level < get_crm_log_level()
- && op->interval_ms == 0 && pcmk__str_eq(op->op_type, CRMD_ACTION_START, pcmk__str_none)) {
- char *digest_source = dump_xml_unformatted(args_xml);
-
- do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s",
- digest, ID(update), magic, digest_source);
- free(digest_source);
- }
-#endif
- crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
-
- free_xml(args_xml);
- free(digest);
-}
-
-#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-
-/*!
- * \internal
- * \brief Create XML for resource operation history update
- *
- * \param[in,out] parent Parent XML node to add to
- * \param[in,out] op Operation event data
- * \param[in] caller_version DC feature set
- * \param[in] target_rc Expected result of operation
- * \param[in] node Name of node on which operation was performed
- * \param[in] origin Arbitrary description of update source
- * \param[in] level A log message will be logged at this level
- *
- * \return Newly created XML node for history update
- */
-xmlNode *
-pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op,
- const char *caller_version, int target_rc,
- const char *node, const char *origin, int level)
-{
- char *key = NULL;
- char *magic = NULL;
- char *op_id = NULL;
- char *op_id_additional = NULL;
- char *local_user_data = NULL;
- const char *exit_reason = NULL;
-
- xmlNode *xml_op = NULL;
- const char *task = NULL;
-
- CRM_CHECK(op != NULL, return NULL);
- do_crm_log(level, "%s: Updating resource %s after %s op %s (interval=%u)",
- origin, op->rsc_id, op->op_type,
- pcmk_exec_status_str(op->op_status), op->interval_ms);
-
- crm_trace("DC version: %s", caller_version);
-
- task = op->op_type;
-
- /* Record a successful agent reload as a start, and a failed one as a
- * monitor, to make life easier for the scheduler when determining the
- * current state.
- *
- * @COMPAT We should check "reload" here only if the operation was for a
- * pre-OCF-1.1 resource agent, but we don't know that here, and we should
- * only ever get results for actions scheduled by us, so we can reasonably
- * assume any "reload" is actually a pre-1.1 agent reload.
- */
- if (pcmk__str_any_of(task, CRMD_ACTION_RELOAD, CRMD_ACTION_RELOAD_AGENT,
- NULL)) {
- if (op->op_status == PCMK_EXEC_DONE) {
- task = CRMD_ACTION_START;
- } else {
- task = CRMD_ACTION_STATUS;
- }
- }
-
- key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
- if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_none)) {
- const char *n_type = crm_meta_value(op->params, "notify_type");
- const char *n_task = crm_meta_value(op->params, "notify_operation");
-
- CRM_LOG_ASSERT(n_type != NULL);
- CRM_LOG_ASSERT(n_task != NULL);
- op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
-
- if (op->op_status != PCMK_EXEC_PENDING) {
- /* Ignore notify errors.
- *
- * @TODO It might be better to keep the correct result here, and
- * ignore it in process_graph_event().
- */
- lrmd__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
- }
-
- } else if (did_rsc_op_fail(op, target_rc)) {
- op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
- if (op->interval_ms == 0) {
- // Ensure 'last' gets updated, in case record-pending is true
- op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
- }
- exit_reason = op->exit_reason;
-
- } else if (op->interval_ms > 0) {
- op_id = strdup(key);
-
- } else {
- op_id = pcmk__op_key(op->rsc_id, "last", 0);
- }
-
- again:
- xml_op = pcmk__xe_match(parent, XML_LRM_TAG_RSC_OP, XML_ATTR_ID, op_id);
- if (xml_op == NULL) {
- xml_op = create_xml_node(parent, XML_LRM_TAG_RSC_OP);
- }
-
- if (op->user_data == NULL) {
- crm_debug("Generating fake transition key for: " PCMK__OP_FMT
- " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
- op->call_id, origin);
- local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
- FAKE_TE_ID);
- op->user_data = local_user_data;
- }
-
- if(magic == NULL) {
- magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
- }
-
- crm_xml_add(xml_op, XML_ATTR_ID, op_id);
- crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
- crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
- crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
- crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
- crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
- crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
- crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason == NULL ? "" : exit_reason);
- crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
-
- crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
- crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
- crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
- crm_xml_add_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, op->interval_ms);
-
- if (compare_version("2.1", caller_version) <= 0) {
- if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
- crm_trace("Timing data (" PCMK__OP_FMT
- "): last=%u change=%u exec=%u queue=%u",
- op->rsc_id, op->op_type, op->interval_ms,
- op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
-
- if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
- // Recurring ops may have changed rc after initial run
- crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE,
- (long long) op->t_rcchange);
- } else {
- crm_xml_add_ll(xml_op, XML_RSC_OP_LAST_CHANGE,
- (long long) op->t_run);
- }
-
- crm_xml_add_int(xml_op, XML_RSC_OP_T_EXEC, op->exec_time);
- crm_xml_add_int(xml_op, XML_RSC_OP_T_QUEUE, op->queue_time);
- }
- }
-
- if (pcmk__str_any_of(op->op_type, CRMD_ACTION_MIGRATE, CRMD_ACTION_MIGRATED, NULL)) {
- /*
- * Record migrate_source and migrate_target always for migrate ops.
- */
- const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
-
- crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
-
- name = XML_LRM_ATTR_MIGRATE_TARGET;
- crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
- }
-
- append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
-
- if (op_id_additional) {
- free(op_id);
- op_id = op_id_additional;
- op_id_additional = NULL;
- goto again;
- }
-
- if (local_user_data) {
- free(local_user_data);
- op->user_data = NULL;
- }
- free(magic);
- free(op_id);
- free(key);
- return xml_op;
-}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 4:59 PM (10 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1019012
Default Alt Text
(57 KB)

Event Timeline