Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/daemons/controld/controld_attrd.c b/daemons/controld/controld_attrd.c
index d0020d82b7..7758161629 100644
--- a/daemons/controld/controld_attrd.c
+++ b/daemons/controld/controld_attrd.c
@@ -1,208 +1,172 @@
/*
* Copyright 2006-2022 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/crm.h>
#include <crm/common/attrd_internal.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_attrd_internal.h>
#include <crm/msg_xml.h>
#include <pacemaker-controld.h>
static pcmk_ipc_api_t *attrd_api = NULL;
enum attrd_command {
cmd_clear,
cmd_purge,
cmd_update
};
void
controld_close_attrd_ipc()
{
if (attrd_api != NULL) {
crm_trace("Closing connection to pacemaker-attrd");
pcmk_disconnect_ipc(attrd_api);
pcmk_free_ipc_api(attrd_api);
attrd_api = NULL;
}
}
static void
log_attrd_error(const char *host, const char *name, const char *value,
gboolean is_remote, char command, int rc)
{
const char *node_type = (is_remote? "Pacemaker Remote" : "cluster");
gboolean shutting_down = pcmk_is_set(fsa_input_register, R_SHUTDOWN);
const char *when = (shutting_down? " at shutdown" : "");
switch (command) {
case 0:
crm_err("Could not clear failure attributes for %s on %s node %s%s: %s "
CRM_XS " rc=%d", (name? name : "all resources"), node_type,
host, when, pcmk_rc_str(rc), rc);
break;
case 'C':
crm_err("Could not purge %s node %s in attribute manager%s: %s "
CRM_XS " rc=%d",
node_type, host, when, pcmk_rc_str(rc), rc);
break;
case 'U':
/* We weren't able to update an attribute after several retries,
* so something is horribly wrong with the attribute manager or the
* underlying system.
*/
do_crm_log(AM_I_DC? LOG_CRIT : LOG_ERR,
"Could not update attribute %s=%s for %s node %s%s: %s "
CRM_XS " rc=%d", name, value, node_type, host, when,
pcmk_rc_str(rc), rc);
if (AM_I_DC) {
/* We are unable to provide accurate information to the
* scheduler, so allow another node to take over DC.
* @TODO Should we do this unconditionally on any failure?
*/
crmd_exit(CRM_EX_FATAL);
} else if (shutting_down) {
// Fast-track shutdown since unable to request via attribute
register_fsa_input(C_FSA_INTERNAL, I_FAIL, NULL);
}
break;
}
}
static void
update_attrd_helper(const char *host, const char *name, const char *value,
const char *interval_spec, const char *user_name,
gboolean is_remote_node, enum attrd_command command)
{
int rc;
int attrd_opts = pcmk__node_attr_none;
if (is_remote_node) {
pcmk__set_node_attr_flags(attrd_opts, pcmk__node_attr_remote);
}
if (attrd_api == NULL) {
rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd);
if (rc != pcmk_rc_ok) {
log_attrd_error(host, name, value, is_remote_node, command, rc);
return;
}
}
- for (int attempt = 1; attempt <= 4; ++attempt) {
- rc = pcmk_rc_ok;
-
- // If we're not already connected, try to connect
- if (!pcmk_ipc_is_connected(attrd_api)) {
- if (attempt == 1) {
- // Start with a clean slate
- pcmk_disconnect_ipc(attrd_api);
- }
-
- // Connect without a main loop, and with no callback either.
- // We don't use any commands that expect a reply.
- rc = pcmk_connect_ipc(attrd_api, pcmk_ipc_dispatch_sync);
-
- crm_debug("Attribute manager connection attempt %d of 4: %s (%d)",
- attempt, pcmk_rc_str(rc), rc);
- }
-
- if (rc == pcmk_rc_ok) {
- switch (command) {
- case cmd_clear:
- /* name/value is really resource/operation */
- rc = pcmk__attrd_api_clear_failures(attrd_api, host, name,
- value, interval_spec,
- user_name, attrd_opts);
- break;
-
- case cmd_update:
- rc = pcmk__attrd_api_update(attrd_api, host, name, value,
- NULL, NULL, user_name,
- attrd_opts | pcmk__node_attr_value);
- break;
-
- case cmd_purge:
- rc = pcmk__attrd_api_purge(attrd_api, host);
- break;
- }
-
- crm_debug("Attribute manager request attempt %d of 4: %s (%d)",
- attempt, pcmk_rc_str(rc), rc);
- }
-
- if (rc == pcmk_rc_ok) {
- // Success, we're done
+ switch (command) {
+ case cmd_clear:
+ /* name/value is really resource/operation */
+ rc = pcmk__attrd_api_clear_failures(attrd_api, host, name,
+ value, interval_spec,
+ user_name, attrd_opts);
break;
- } else if ((rc != EAGAIN) && (rc != EALREADY)) {
- /* EAGAIN or EALREADY indicates a temporary block, so just try
- * again. Otherwise, close the connection for a clean slate.
- */
- pcmk_disconnect_ipc(attrd_api);
- }
+ case cmd_update:
+ rc = pcmk__attrd_api_update(attrd_api, host, name, value,
+ NULL, NULL, user_name,
+ attrd_opts | pcmk__node_attr_value);
+ break;
- /* @TODO If the attribute manager remains unavailable the entire time,
- * this function takes more than 6 seconds. Maybe set a timer for
- * retries, to let the main loop do other work.
- */
- if (attempt < 4) {
- sleep(attempt);
- }
+ case cmd_purge:
+ rc = pcmk__attrd_api_purge(attrd_api, host);
+ break;
}
if (rc != pcmk_rc_ok) {
log_attrd_error(host, name, value, is_remote_node, command, rc);
}
}
void
update_attrd(const char *host, const char *name, const char *value,
const char *user_name, gboolean is_remote_node)
{
update_attrd_helper(host, name, value, NULL, user_name, is_remote_node,
cmd_update);
}
+void
+update_attrd_list(GList *attrs, uint32_t opts)
+{
+ pcmk__attrd_api_update_list(attrd_api, attrs, NULL, NULL, NULL,
+ opts | pcmk__node_attr_value);
+}
+
void
update_attrd_remote_node_removed(const char *host, const char *user_name)
{
crm_trace("Asking attribute manager to purge Pacemaker Remote node %s",
host);
update_attrd_helper(host, NULL, NULL, NULL, user_name, TRUE, cmd_purge);
}
void
update_attrd_clear_failures(const char *host, const char *rsc, const char *op,
const char *interval_spec, gboolean is_remote_node)
{
const char *op_desc = NULL;
const char *interval_desc = NULL;
const char *node_type = is_remote_node? "Pacemaker Remote" : "cluster";
if (op) {
interval_desc = interval_spec? interval_spec : "nonrecurring";
op_desc = op;
} else {
interval_desc = "all";
op_desc = "operations";
}
crm_info("Asking pacemaker-attrd to clear failure of %s %s for %s on %s node %s",
interval_desc, op_desc, rsc, node_type, host);
update_attrd_helper(host, rsc, op, interval_spec, NULL, is_remote_node, cmd_clear);
}
diff --git a/daemons/controld/controld_te_events.c b/daemons/controld/controld_te_events.c
index a62319b858..633b409c93 100644
--- a/daemons/controld/controld_te_events.c
+++ b/daemons/controld/controld_te_events.c
@@ -1,512 +1,544 @@
/*
* Copyright 2004-2022 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 <pacemaker-controld.h>
+#include <crm/common/attrd_internal.h>
+#include <crm/common/ipc_attrd_internal.h>
+
char *failed_stop_offset = NULL;
char *failed_start_offset = NULL;
gboolean
fail_incompletable_actions(pcmk__graph_t *graph, const char *down_node)
{
const char *target_uuid = NULL;
const char *router = NULL;
const char *router_uuid = NULL;
xmlNode *last_action = NULL;
GList *gIter = NULL;
GList *gIter2 = NULL;
if (graph == NULL || graph->complete) {
return FALSE;
}
gIter = graph->synapses;
for (; gIter != NULL; gIter = gIter->next) {
pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) gIter->data;
if (pcmk_any_flags_set(synapse->flags, pcmk__synapse_confirmed|pcmk__synapse_failed)) {
/* We've already been here */
continue;
}
gIter2 = synapse->actions;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
pcmk__graph_action_t *action = (pcmk__graph_action_t *) gIter2->data;
if ((action->type == pcmk__pseudo_graph_action)
|| pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
continue;
} else if (action->type == pcmk__cluster_graph_action) {
const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
if (pcmk__str_eq(task, CRM_OP_FENCE, pcmk__str_casei)) {
continue;
}
}
target_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
router = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
if (router) {
crm_node_t *node = crm_get_peer(0, router);
if (node) {
router_uuid = node->uuid;
}
}
if (pcmk__str_eq(target_uuid, down_node, pcmk__str_casei) || pcmk__str_eq(router_uuid, down_node, pcmk__str_casei)) {
pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
pcmk__set_synapse_flags(synapse, pcmk__synapse_failed);
last_action = action->xml;
stop_te_timer(action);
pcmk__update_graph(graph, action);
if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
crm_notice("Action %d (%s) was pending on %s (offline)",
action->id, crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY), down_node);
} else {
crm_info("Action %d (%s) is scheduled for %s (offline)",
action->id, crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY), down_node);
}
}
}
}
if (last_action != NULL) {
crm_info("Node %s shutdown resulted in un-runnable actions", down_node);
abort_transition(INFINITY, pcmk__graph_restart, "Node failure",
last_action);
return TRUE;
}
return FALSE;
}
/*!
* \internal
* \brief Update failure-related node attributes if warranted
*
* \param[in] event XML describing operation that (maybe) failed
* \param[in] event_node_uuid Node that event occurred on
* \param[in] rc Actual operation return code
* \param[in] target_rc Expected operation return code
* \param[in] do_update If TRUE, do update regardless of operation type
* \param[in] ignore_failures If TRUE, update last failure but not fail count
*
* \return TRUE if this was not a direct nack, success or lrm status refresh
*/
static gboolean
update_failcount(xmlNode * event, const char *event_node_uuid, int rc,
int target_rc, gboolean do_update, gboolean ignore_failures)
{
guint interval_ms = 0;
char *task = NULL;
char *rsc_id = NULL;
const char *value = NULL;
const char *id = crm_element_value(event, XML_LRM_ATTR_TASK_KEY);
const char *on_uname = crm_peer_uname(event_node_uuid);
const char *origin = crm_element_value(event, XML_ATTR_ORIGIN);
// Nothing needs to be done for success or status refresh
if (rc == target_rc) {
return FALSE;
} else if (pcmk__str_eq(origin, "build_active_RAs", pcmk__str_casei)) {
crm_debug("No update for %s (rc=%d) on %s: Old failure from lrm status refresh",
id, rc, on_uname);
return FALSE;
}
/* Sanity check */
CRM_CHECK(on_uname != NULL, return TRUE);
CRM_CHECK(parse_op_key(id, &rsc_id, &task, &interval_ms),
crm_err("Couldn't parse: %s", ID(event)); goto bail);
/* Decide whether update is necessary and what value to use */
if ((interval_ms > 0) || pcmk__str_eq(task, CRMD_ACTION_PROMOTE, pcmk__str_casei)
|| pcmk__str_eq(task, CRMD_ACTION_DEMOTE, pcmk__str_casei)) {
do_update = TRUE;
} else if (pcmk__str_eq(task, CRMD_ACTION_START, pcmk__str_casei)) {
do_update = TRUE;
if (failed_start_offset == NULL) {
failed_start_offset = strdup(CRM_INFINITY_S);
}
value = failed_start_offset;
} else if (pcmk__str_eq(task, CRMD_ACTION_STOP, pcmk__str_casei)) {
do_update = TRUE;
if (failed_stop_offset == NULL) {
failed_stop_offset = strdup(CRM_INFINITY_S);
}
value = failed_stop_offset;
}
/* Fail count will be either incremented or set to infinity */
if (!pcmk_str_is_infinity(value)) {
value = XML_NVPAIR_ATTR_VALUE "++";
}
if (do_update) {
+ pcmk__attrd_query_pair_t *fail_pair = NULL;
+ pcmk__attrd_query_pair_t *last_pair = NULL;
+ char *fail_name = NULL;
+ char *last_name = NULL;
+ GList *attrs = NULL;
+
+ uint32_t opts = pcmk__node_attr_none;
+
char *now = pcmk__ttoa(time(NULL));
- char *attr_name = NULL;
- gboolean is_remote_node = FALSE;
if (g_hash_table_lookup(crm_remote_peer_cache, event_node_uuid)) {
- is_remote_node = TRUE;
+ opts |= pcmk__node_attr_remote;
}
crm_info("Updating %s for %s on %s after failed %s: rc=%d (update=%s, time=%s)",
(ignore_failures? "last failure" : "failcount"),
rsc_id, on_uname, task, rc, value, now);
/* Update the fail count, if we're not ignoring failures */
if (!ignore_failures) {
- attr_name = pcmk__failcount_name(rsc_id, task, interval_ms);
- update_attrd(on_uname, attr_name, value, NULL, is_remote_node);
- free(attr_name);
+ fail_pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
+ CRM_ASSERT(fail_pair != NULL);
+
+ fail_name = pcmk__failcount_name(rsc_id, task, interval_ms);
+ fail_pair->name = fail_name;
+ fail_pair->value = value;
+ fail_pair->node = on_uname;
+
+ attrs = g_list_prepend(attrs, fail_pair);
}
/* Update the last failure time (even if we're ignoring failures,
* so that failure can still be detected and shown, e.g. by crm_mon)
*/
- attr_name = pcmk__lastfailure_name(rsc_id, task, interval_ms);
- update_attrd(on_uname, attr_name, now, NULL, is_remote_node);
- free(attr_name);
+ last_pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
+ CRM_ASSERT(last_pair != NULL);
+
+ last_name = pcmk__lastfailure_name(rsc_id, task, interval_ms);
+ last_pair->name = last_name;
+ last_pair->value = now;
+ last_pair->node = on_uname;
+
+ attrs = g_list_prepend(attrs, last_pair);
+
+ update_attrd_list(attrs, opts);
+
+ if (!ignore_failures) {
+ free(fail_name);
+ free(fail_pair);
+ }
+
+ free(last_name);
+ free(last_pair);
+ g_list_free(attrs);
free(now);
}
bail:
free(rsc_id);
free(task);
return TRUE;
}
pcmk__graph_action_t *
controld_get_action(int id)
{
for (GList *item = transition_graph->synapses; item; item = item->next) {
pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) item->data;
for (GList *item2 = synapse->actions; item2; item2 = item2->next) {
pcmk__graph_action_t *action = (pcmk__graph_action_t *) item2->data;
if (action->id == id) {
return action;
}
}
}
return NULL;
}
pcmk__graph_action_t *
get_cancel_action(const char *id, const char *node)
{
GList *gIter = NULL;
GList *gIter2 = NULL;
gIter = transition_graph->synapses;
for (; gIter != NULL; gIter = gIter->next) {
pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) gIter->data;
gIter2 = synapse->actions;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
const char *task = NULL;
const char *target = NULL;
pcmk__graph_action_t *action = (pcmk__graph_action_t *) gIter2->data;
task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
if (!pcmk__str_eq(CRMD_ACTION_CANCEL, task, pcmk__str_casei)) {
continue;
}
task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
if (!pcmk__str_eq(task, id, pcmk__str_casei)) {
crm_trace("Wrong key %s for %s on %s", task, id, node);
continue;
}
target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
if (node && !pcmk__str_eq(target, node, pcmk__str_casei)) {
crm_trace("Wrong node %s for %s on %s", target, id, node);
continue;
}
crm_trace("Found %s on %s", id, node);
return action;
}
}
return NULL;
}
bool
confirm_cancel_action(const char *id, const char *node_id)
{
const char *op_key = NULL;
const char *node_name = NULL;
pcmk__graph_action_t *cancel = get_cancel_action(id, node_id);
if (cancel == NULL) {
return FALSE;
}
op_key = crm_element_value(cancel->xml, XML_LRM_ATTR_TASK_KEY);
node_name = crm_element_value(cancel->xml, XML_LRM_ATTR_TARGET);
stop_te_timer(cancel);
te_action_confirmed(cancel, transition_graph);
crm_info("Cancellation of %s on %s confirmed (action %d)",
op_key, node_name, cancel->id);
return TRUE;
}
/* downed nodes are listed like: <downed> <node id="UUID1" /> ... </downed> */
#define XPATH_DOWNED "//" XML_GRAPH_TAG_DOWNED \
"/" XML_CIB_TAG_NODE "[@" XML_ATTR_UUID "='%s']"
/*!
* \brief Find a transition event that would have made a specified node down
*
* \param[in] target UUID of node to match
*
* \return Matching event if found, NULL otherwise
*/
pcmk__graph_action_t *
match_down_event(const char *target)
{
pcmk__graph_action_t *match = NULL;
xmlXPathObjectPtr xpath_ret = NULL;
GList *gIter, *gIter2;
char *xpath = crm_strdup_printf(XPATH_DOWNED, target);
for (gIter = transition_graph->synapses;
gIter != NULL && match == NULL;
gIter = gIter->next) {
for (gIter2 = ((pcmk__graph_synapse_t * ) gIter->data)->actions;
gIter2 != NULL && match == NULL;
gIter2 = gIter2->next) {
match = (pcmk__graph_action_t *) gIter2->data;
if (pcmk_is_set(match->flags, pcmk__graph_action_executed)) {
xpath_ret = xpath_search(match->xml, xpath);
if (numXpathResults(xpath_ret) < 1) {
match = NULL;
}
freeXpathObject(xpath_ret);
} else {
// Only actions that were actually started can match
match = NULL;
}
}
}
free(xpath);
if (match != NULL) {
crm_debug("Shutdown action %d (%s) found for node %s", match->id,
crm_element_value(match->xml, XML_LRM_ATTR_TASK_KEY), target);
} else {
crm_debug("No reason to expect node %s to be down", target);
}
return match;
}
void
process_graph_event(xmlNode *event, const char *event_node)
{
int rc = -1; // Actual result
int target_rc = -1; // Expected result
int status = -1; // Executor status
int callid = -1; // Executor call ID
int transition_num = -1; // Transition number
int action_num = -1; // Action number within transition
char *update_te_uuid = NULL;
bool ignore_failures = FALSE;
const char *id = NULL;
const char *desc = NULL;
const char *magic = NULL;
const char *uname = NULL;
CRM_ASSERT(event != NULL);
/*
<lrm_rsc_op id="rsc_east-05_last_0" operation_key="rsc_east-05_monitor_0" operation="monitor" crm-debug-origin="do_update_resource" crm_feature_set="3.0.6" transition-key="9:2:7:be2e97d9-05e2-439d-863e-48f7aecab2aa" transition-magic="0:7;9:2:7:be2e97d9-05e2-439d-863e-48f7aecab2aa" call-id="17" rc-code="7" op-status="0" interval="0" last-rc-change="1355361636" exec-time="128" queue-time="0" op-digest="c81f5f40b1c9e859c992e800b1aa6972"/>
*/
magic = crm_element_value(event, XML_ATTR_TRANSITION_KEY);
if (magic == NULL) {
/* non-change */
return;
}
crm_element_value_int(event, XML_LRM_ATTR_OPSTATUS, &status);
if (status == PCMK_EXEC_PENDING) {
return;
}
id = crm_element_value(event, XML_LRM_ATTR_TASK_KEY);
crm_element_value_int(event, XML_LRM_ATTR_RC, &rc);
crm_element_value_int(event, XML_LRM_ATTR_CALLID, &callid);
rc = pcmk__effective_rc(rc);
if (decode_transition_key(magic, &update_te_uuid, &transition_num,
&action_num, &target_rc) == FALSE) {
// decode_transition_key() already logged the bad key
crm_err("Can't process action %s result: Incompatible versions? "
CRM_XS " call-id=%d", id, callid);
abort_transition(INFINITY, pcmk__graph_restart, "Bad event", event);
return;
}
if (transition_num == -1) {
// E.g. crm_resource --fail
desc = "initiated outside of the cluster";
abort_transition(INFINITY, pcmk__graph_restart, "Unexpected event",
event);
} else if ((action_num < 0) || !pcmk__str_eq(update_te_uuid, te_uuid, pcmk__str_none)) {
desc = "initiated by a different DC";
abort_transition(INFINITY, pcmk__graph_restart, "Foreign event", event);
} else if ((transition_graph->id != transition_num)
|| transition_graph->complete) {
// Action is not from currently active transition
guint interval_ms = 0;
if (parse_op_key(id, NULL, NULL, &interval_ms)
&& (interval_ms != 0)) {
/* Recurring actions have the transition number they were first
* scheduled in.
*/
if (status == PCMK_EXEC_CANCELLED) {
confirm_cancel_action(id, get_node_id(event));
goto bail;
}
desc = "arrived after initial scheduling";
abort_transition(INFINITY, pcmk__graph_restart,
"Change in recurring result", event);
} else if (transition_graph->id != transition_num) {
desc = "arrived really late";
abort_transition(INFINITY, pcmk__graph_restart, "Old event", event);
} else {
desc = "arrived late";
abort_transition(INFINITY, pcmk__graph_restart, "Inactive graph",
event);
}
} else {
// Event is result of an action from currently active transition
pcmk__graph_action_t *action = controld_get_action(action_num);
if (action == NULL) {
// Should never happen
desc = "unknown";
abort_transition(INFINITY, pcmk__graph_restart, "Unknown event",
event);
} else if (pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
/* Nothing further needs to be done if the action has already been
* confirmed. This can happen e.g. when processing both an
* "xxx_last_0" or "xxx_last_failure_0" record as well as the main
* history record, which would otherwise result in incorrectly
* bumping the fail count twice.
*/
crm_log_xml_debug(event, "Event already confirmed:");
goto bail;
} else {
/* An action result needs to be confirmed.
* (This is the only case where desc == NULL.)
*/
if (pcmk__str_eq(crm_meta_value(action->params, XML_OP_ATTR_ON_FAIL), "ignore", pcmk__str_casei)) {
ignore_failures = TRUE;
} else if (rc != target_rc) {
pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
}
stop_te_timer(action);
te_action_confirmed(action, transition_graph);
if (pcmk_is_set(action->flags, pcmk__graph_action_failed)) {
abort_transition(action->synapse->priority + 1,
pcmk__graph_restart, "Event failed", event);
}
}
}
if (id == NULL) {
id = "unknown action";
}
uname = crm_element_value(event, XML_LRM_ATTR_TARGET);
if (uname == NULL) {
uname = "unknown node";
}
if (status == PCMK_EXEC_INVALID) {
// We couldn't attempt the action
crm_info("Transition %d action %d (%s on %s): %s",
transition_num, action_num, id, uname,
pcmk_exec_status_str(status));
} else if (desc && update_failcount(event, event_node, rc, target_rc,
(transition_num == -1), FALSE)) {
crm_notice("Transition %d action %d (%s on %s): expected '%s' but got '%s' "
CRM_XS " target-rc=%d rc=%d call-id=%d event='%s'",
transition_num, action_num, id, uname,
services_ocf_exitcode_str(target_rc),
services_ocf_exitcode_str(rc),
target_rc, rc, callid, desc);
} else if (desc) {
crm_info("Transition %d action %d (%s on %s): %s "
CRM_XS " rc=%d target-rc=%d call-id=%d",
transition_num, action_num, id, uname,
desc, rc, target_rc, callid);
} else if (rc == target_rc) {
crm_info("Transition %d action %d (%s on %s) confirmed: %s "
CRM_XS " rc=%d call-id=%d",
transition_num, action_num, id, uname,
services_ocf_exitcode_str(rc), rc, callid);
} else {
update_failcount(event, event_node, rc, target_rc,
(transition_num == -1), ignore_failures);
crm_notice("Transition %d action %d (%s on %s): expected '%s' but got '%s' "
CRM_XS " target-rc=%d rc=%d call-id=%d",
transition_num, action_num, id, uname,
services_ocf_exitcode_str(target_rc),
services_ocf_exitcode_str(rc),
target_rc, rc, callid);
}
bail:
free(update_te_uuid);
}
diff --git a/daemons/controld/controld_utils.h b/daemons/controld/controld_utils.h
index a695194a20..a11210982c 100644
--- a/daemons/controld/controld_utils.h
+++ b/daemons/controld/controld_utils.h
@@ -1,127 +1,128 @@
/*
- * Copyright 2004-2020 the Pacemaker project contributors
+ * Copyright 2004-2022 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef CRMD_UTILS__H
# define CRMD_UTILS__H
# include <crm/crm.h>
# include <crm/common/xml.h>
# include <crm/cib/internal.h> // CIB_OP_MODIFY
# include <controld_fsa.h> // fsa_cib_conn
# include <controld_alerts.h>
# define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# define fsa_cib_update(section, data, options, call_id, user_name) \
if(fsa_cib_conn != NULL) { \
call_id = cib_internal_op( \
fsa_cib_conn, CIB_OP_MODIFY, NULL, section, data, \
NULL, options, user_name); \
\
} else { \
crm_err("No CIB manager connection available"); \
}
static inline void
fsa_cib_anon_update(const char *section, xmlNode *data) {
if (fsa_cib_conn == NULL) {
crm_err("No CIB connection available");
} else {
int opts = cib_scope_local | cib_quorum_override | cib_can_create;
fsa_cib_conn->cmds->modify(fsa_cib_conn, section, data, opts);
}
}
static inline void
fsa_cib_anon_update_discard_reply(const char *section, xmlNode *data) {
if (fsa_cib_conn == NULL) {
crm_err("No CIB connection available");
} else {
int opts = cib_scope_local | cib_quorum_override | cib_can_create | cib_discard_reply;
fsa_cib_conn->cmds->modify(fsa_cib_conn, section, data, opts);
}
}
extern gboolean fsa_has_quorum;
extern bool controld_shutdown_lock_enabled;
extern int last_peer_update;
extern int last_resource_update;
enum node_update_flags {
node_update_none = 0x0000,
node_update_quick = 0x0001,
node_update_cluster = 0x0010,
node_update_peer = 0x0020,
node_update_join = 0x0040,
node_update_expected = 0x0100,
node_update_all = node_update_cluster|node_update_peer|node_update_join|node_update_expected,
};
crm_exit_t crmd_exit(crm_exit_t exit_code);
_Noreturn void crmd_fast_exit(crm_exit_t exit_code);
void controld_shutdown_schedulerd_ipc(void);
void controld_stop_sched_timer(void);
void controld_free_sched_timer(void);
void controld_expect_sched_reply(char *ref);
void fsa_dump_actions(uint64_t action, const char *text);
void fsa_dump_inputs(int log_level, const char *text, long long input_register);
gboolean update_dc(xmlNode * msg);
void crm_update_peer_join(const char *source, crm_node_t * node, enum crm_join_phase phase);
xmlNode *create_node_state_update(crm_node_t *node, int flags,
xmlNode *parent, const char *source);
void populate_cib_nodes(enum node_update_flags flags, const char *source);
void crm_update_quorum(gboolean quorum, gboolean force_update);
void controld_close_attrd_ipc(void);
void update_attrd(const char *host, const char *name, const char *value, const char *user_name, gboolean is_remote_node);
+void update_attrd_list(GList *attrs, uint32_t opts);
void update_attrd_remote_node_removed(const char *host, const char *user_name);
void update_attrd_clear_failures(const char *host, const char *rsc,
const char *op, const char *interval_spec,
gboolean is_remote_node);
int crmd_join_phase_count(enum crm_join_phase phase);
void crmd_join_phase_log(int level);
void crmd_peer_down(crm_node_t *peer, bool full);
unsigned int cib_op_timeout(void);
bool feature_set_compatible(const char *dc_version, const char *join_version);
bool controld_action_is_recordable(const char *action);
// Subsections of node_state
enum controld_section_e {
controld_section_lrm,
controld_section_lrm_unlocked,
controld_section_attrs,
controld_section_all,
controld_section_all_unlocked
};
void controld_delete_node_state(const char *uname,
enum controld_section_e section, int options);
int controld_delete_resource_history(const char *rsc_id, const char *node,
const char *user_name, int call_options);
const char *get_node_id(xmlNode *lrm_rsc_op);
/* Convenience macro for registering a CIB callback
* (assumes that data can be freed with free())
*/
# define fsa_register_cib_callback(id, flag, data, fn) do { \
CRM_ASSERT(fsa_cib_conn); \
fsa_cib_conn->cmds->register_callback_full( \
fsa_cib_conn, id, cib_op_timeout(), \
flag, data, #fn, fn, free); \
} while(0)
#endif
diff --git a/include/crm/common/ipc_attrd_internal.h b/include/crm/common/ipc_attrd_internal.h
index 47ec57f108..10db3fec1a 100644
--- a/include/crm/common/ipc_attrd_internal.h
+++ b/include/crm/common/ipc_attrd_internal.h
@@ -1,170 +1,195 @@
/*
* Copyright 2022 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H
# define PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H
#include <glib.h> // GList
#include <crm/common/ipc.h> // pcmk_ipc_api_t
#ifdef __cplusplus
extern "C" {
#endif
//! Possible types of attribute manager replies
enum pcmk__attrd_api_reply {
pcmk__attrd_reply_unknown,
pcmk__attrd_reply_query,
};
// Information passed with pcmk__attrd_reply_query
typedef struct {
const char *node;
const char *name;
const char *value;
} pcmk__attrd_query_pair_t;
/*!
* Attribute manager reply passed to event callback
*
* \note The pointers in the reply are only guaranteed to be meaningful for the
* execution of the callback; if the values are needed for later, the
* callback should copy them.
*/
typedef struct {
enum pcmk__attrd_api_reply reply_type;
union {
// pcmk__attrd_reply_query
GList *pairs;
} data;
} pcmk__attrd_api_reply_t;
/*!
* \internal
* \brief Send a request to pacemaker-attrd to clear resource failure
*
- * \param[in] api Connection to pacemaker-attrd (or NULL to use
- * a temporary new connection)
+ * \param[in] api pacemaker-attrd IPC object
* \param[in] node Affect only this node (or NULL for all nodes)
* \param[in] resource Name of resource to clear (or NULL for all)
* \param[in] operation Name of operation to clear (or NULL for all)
* \param[in] interval_spec If operation is not NULL, its interval
* \param[in] user_name ACL user to pass to pacemaker-attrd
* \param[in] options Bitmask of pcmk__node_attr_opts
*
+ * \note If \p api is NULL, a new temporary connection will be created
+ * just for this operation and destroyed afterwards. If \p api is
+ * not NULL but is not yet connected to pacemaker-attrd, the object
+ * will be connected for this operation and left connected afterwards.
+ * This allows for reusing an IPC connection.
+ *
* \return Standard Pacemaker return code
*/
int pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node,
const char *resource, const char *operation,
const char *interval_spec, const char *user_name,
uint32_t options);
/*!
* \internal
*
* \brief Delete a previously set attribute by setting its value to NULL
*
* \param[in] api Connection to pacemaker-attrd (or NULL to use
* a temporary new connection)
* \param[in] node Delete attribute for this node (or NULL for current node)
* \param[in] name Attribute name
* \param[in] options Bitmask of pcmk__node_attr_opts
*
* \return Standard Pacemaker return code
*/
int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
uint32_t options);
/*!
* \internal
* \brief Purge a node from pacemaker-attrd
*
- * \param[in] api Connection to pacemaker-attrd (or NULL to use
- * a temporary new connection)
+ * \param[in] api pacemaker-attrd IPC object
* \param[in] node Node to remove
*
+ * \note If \p api is NULL, a new temporary connection will be created
+ * just for this operation and destroyed afterwards. If \p api is
+ * not NULL but is not yet connected to pacemaker-attrd, the object
+ * will be connected for this operation and left connected afterwards.
+ * This allows for reusing an IPC connection.
+ *
* \return Standard Pacemaker return code
*/
int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node);
/*!
* \internal
* \brief Get the value of an attribute from pacemaker-attrd
*
* \param[in] api Connection to pacemaker-attrd
* \param[in] node Look up the attribute for this node
* (or NULL for all nodes)
* \param[in] name Attribute name
* \param[in] options Bitmask of pcmk__node_attr_opts
*
* \return Standard Pacemaker return code
*/
int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
uint32_t options);
/*!
* \internal
* \brief Tell pacemaker-attrd to update the CIB with current values
*
- * \param[in] api Connection to pacemaker-attrd (or NULL to use
- * a temporary new connection)
+ * \param[in] api pacemaker-attrd IPC object
* \param[in] node Affect only this node (or NULL for all nodes)
*
+ * \note If \p api is NULL, a new temporary connection will be created
+ * just for this operation and destroyed afterwards. If \p api is
+ * not NULL but is not yet connected to pacemaker-attrd, the object
+ * will be connected for this operation and left connected afterwards.
+ * This allows for reusing an IPC connection.
+ *
* \return Standard Pacemaker return code
*/
int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node);
/*!
* \internal
* \brief Update an attribute's value, time to wait, or both
*
- * \param[in] api Connection to pacemaker-attrd (or NULL to use
- * a temporary new connection)
+ * \param[in] api pacemaker-attrd IPC object
* \param[in] node Affect only this node (or NULL for current node)
* \param[in] name Attribute name
* \param[in] value The attribute's new value, or NULL to unset
* \param[in] dampen The new time to wait value, or NULL to unset
* \param[in] set ID of attribute set to use (or NULL to choose first)
* \param[in] user_name ACL user to pass to pacemaker-attrd
* \param[in] options Bitmask of pcmk__node_attr_opts
*
+ * \note If \p api is NULL, a new temporary connection will be created
+ * just for this operation and destroyed afterwards. If \p api is
+ * not NULL but is not yet connected to pacemaker-attrd, the object
+ * will be connected for this operation and left connected afterwards.
+ * This allows for reusing an IPC connection.
+ *
* \return Standard Pacemaker return code
*/
int pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
const char *value, const char *dampen, const char *set,
const char *user_name, uint32_t options);
/*!
* \internal
* \brief Like pcmk__attrd_api_update, but for multiple attributes at once
*
- * \param[in] api Connection to pacemaker-attrd (or NULL to use
- * a temporary new connection)
+ * \param[in] api pacemaker-attrd IPC object
* \param[in] attrs A list of pcmk__attr_query_pair_t structs
* \param[in] dampen The new time to wait value, or NULL to unset
* \param[in] set ID of attribute set to use (or NULL to choose first)
* \param[in] user_name ACL user to pass to pacemaker-attrd
* \param[in] options Bitmask of pcmk__node_attr_opts
*
- * \return Standard Pacemaker return code
+ * \note If \p api is NULL, a new temporary connection will be created
+ * just for this operation and destroyed afterwards. If \p api is
+ * not NULL but is not yet connected to pacemaker-attrd, the object
+ * will be connected for this operation and left connected afterwards.
+ * This allows for reusing an IPC connection.
*
* \note Not all attrd versions support setting multiple attributes at once.
* For those servers that do not, this function will fall back to just
* sending a separate IPC request for each attribute.
+ *
+ * \return Standard Pacemaker return code
*/
int pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs,
const char *dampen, const char *set,
const char *user_name, uint32_t options);
#ifdef __cplusplus
}
#endif
#endif // PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H
diff --git a/lib/common/ipc_attrd.c b/lib/common/ipc_attrd.c
index 3b663ca5d6..39a9a73d6e 100644
--- a/lib/common/ipc_attrd.c
+++ b/lib/common/ipc_attrd.c
@@ -1,559 +1,575 @@
/*
* Copyright 2011-2022 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <crm_internal.h>
#include <stdio.h>
#include <crm/crm.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_attrd_internal.h>
#include <crm/common/attrd_internal.h>
#include <crm/msg_xml.h>
#include "crmcommon_private.h"
static void
set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
{
const char *name = NULL;
pcmk__attrd_query_pair_t *pair;
name = crm_element_value(msg_data, PCMK__XA_ATTR_NAME);
for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
node != NULL; node = crm_next_same_xml(node)) {
pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
CRM_ASSERT(pair != NULL);
pair->node = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME);
pair->name = name;
pair->value = crm_element_value(node, PCMK__XA_ATTR_VALUE);
data->data.pairs = g_list_prepend(data->data.pairs, pair);
}
}
static bool
reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
{
const char *command = crm_element_value(request, PCMK__XA_TASK);
return pcmk__str_any_of(command, PCMK__ATTRD_CMD_UPDATE,
PCMK__ATTRD_CMD_UPDATE_BOTH, PCMK__ATTRD_CMD_UPDATE_DELAY,
PCMK__ATTRD_CMD_QUERY, NULL);
}
static bool
dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
{
const char *value = NULL;
crm_exit_t status = CRM_EX_OK;
pcmk__attrd_api_reply_t reply_data = {
pcmk__attrd_reply_unknown
};
if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_none)) {
return false;
}
/* Do some basic validation of the reply */
value = crm_element_value(reply, F_TYPE);
if (value == NULL) {
crm_debug("Unrecognizable attrd message: no message type specified");
status = CRM_EX_PROTOCOL;
goto done;
}
if (!pcmk__str_eq(value, T_ATTRD, pcmk__str_none)) {
crm_debug("Unrecognizable attrd message: invalid message type '%s'",
value);
status = CRM_EX_PROTOCOL;
goto done;
}
/* Only the query command gets a reply for now. */
value = crm_element_value(reply, F_SUBTYPE);
if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
/* This likely means the client gave us an attribute name that doesn't
* exist.
*/
if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
crm_debug("Empty attrd message: no attribute name");
status = ENXIO;
goto done;
}
reply_data.reply_type = pcmk__attrd_reply_query;
set_pairs_data(&reply_data, reply);
} else {
crm_debug("Cannot handle a reply from message type '%s'",
pcmk__s(value, "(unspecified)"));
status = CRM_EX_PROTOCOL;
goto done;
}
done:
pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
/* Free any reply data that was allocated */
if (reply_data.data.pairs) {
g_list_free_full(reply_data.data.pairs, free);
}
return false;
}
pcmk__ipc_methods_t *
pcmk__attrd_api_methods()
{
pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
if (cmds != NULL) {
cmds->new_data = NULL;
cmds->free_data = NULL;
cmds->post_connect = NULL;
cmds->reply_expected = reply_expected;
cmds->dispatch = dispatch;
}
return cmds;
}
/*!
* \internal
* \brief Create a generic pacemaker-attrd operation
*
* \param[in] user_name If not NULL, ACL user to set for operation
*
* \return XML of pacemaker-attrd operation
*/
static xmlNode *
create_attrd_op(const char *user_name)
{
xmlNode *attrd_op = create_xml_node(NULL, __func__);
crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown"));
crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
return attrd_op;
}
static int
create_api(pcmk_ipc_api_t **api)
{
int rc = pcmk_new_ipc_api(api, pcmk_ipc_attrd);
if (rc != pcmk_rc_ok) {
crm_err("error: Could not connect to attrd: %s", pcmk_rc_str(rc));
}
return rc;
}
static void
destroy_api(pcmk_ipc_api_t *api)
{
pcmk_disconnect_ipc(api);
pcmk_free_ipc_api(api);
api = NULL;
}
static int
connect_and_send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
{
int rc = pcmk_rc_ok;
int max = 5;
while (max > 0) {
crm_info("Connecting to cluster... %d retries remaining", max);
rc = pcmk_connect_ipc(api, pcmk_ipc_dispatch_sync);
if (rc == pcmk_rc_ok) {
rc = pcmk__send_ipc_request(api, request);
break;
} else if (rc == EAGAIN || rc == EALREADY) {
sleep(5 - max);
max--;
} else {
crm_err("error: Could not connect to attrd: %s", pcmk_rc_str(rc));
break;
}
}
return rc;
}
static int
send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
{
return pcmk__send_ipc_request(api, request);
}
int
pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node,
const char *resource, const char *operation,
const char *interval_spec, const char *user_name,
uint32_t options)
{
int rc = pcmk_rc_ok;
xmlNode *request = create_attrd_op(user_name);
const char *interval_desc = NULL;
const char *op_desc = NULL;
const char *target = pcmk__node_attr_target(node);
if (target != NULL) {
node = target;
}
crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_CLEAR_FAILURE);
crm_xml_add(request, PCMK__XA_ATTR_NODE_NAME, node);
crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation);
crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec);
crm_xml_add_int(request, PCMK__XA_ATTR_IS_REMOTE,
pcmk_is_set(options, pcmk__node_attr_remote));
if (api == NULL) {
rc = create_api(&api);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = connect_and_send_attrd_request(api, request);
destroy_api(api);
+
+ } else if (!pcmk_ipc_is_connected(api)) {
+ rc = connect_and_send_attrd_request(api, request);
+
} else {
rc = send_attrd_request(api, request);
}
free_xml(request);
if (operation) {
interval_desc = interval_spec? interval_spec : "nonrecurring";
op_desc = operation;
} else {
interval_desc = "all";
op_desc = "operations";
}
crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
interval_desc, op_desc, (resource? resource : "all resources"),
(node? node : "all nodes"), pcmk_rc_str(rc), rc);
return rc;
}
int
pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
uint32_t options)
{
const char *target = NULL;
if (name == NULL) {
return EINVAL;
}
target = pcmk__node_attr_target(node);
if (target != NULL) {
node = target;
}
/* Make sure the right update option is set. */
options &= ~pcmk__node_attr_delay;
options |= pcmk__node_attr_value;
return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
}
int
pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
{
int rc = pcmk_rc_ok;
xmlNode *request = NULL;
const char *display_host = (node ? node : "localhost");
const char *target = pcmk__node_attr_target(node);
if (target != NULL) {
node = target;
}
request = create_attrd_op(NULL);
crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
crm_xml_add(request, PCMK__XA_ATTR_NODE_NAME, node);
if (api == NULL) {
rc = create_api(&api);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = connect_and_send_attrd_request(api, request);
destroy_api(api);
+
+ } else if (!pcmk_ipc_is_connected(api)) {
+ rc = connect_and_send_attrd_request(api, request);
+
} else {
rc = send_attrd_request(api, request);
}
free_xml(request);
crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)",
display_host, pcmk_rc_str(rc), rc);
return rc;
}
int
pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
uint32_t options)
{
int rc = pcmk_rc_ok;
xmlNode *request = NULL;
const char *target = NULL;
if (name == NULL) {
return EINVAL;
}
target = pcmk__node_attr_target(node);
if (target != NULL) {
node = target;
}
request = create_attrd_op(NULL);
crm_xml_add(request, PCMK__XA_ATTR_NAME, name);
crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
crm_xml_add(request, PCMK__XA_ATTR_NODE_NAME, node);
rc = send_attrd_request(api, request);
free_xml(request);
if (node) {
crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
name, node, pcmk_rc_str(rc), rc);
} else {
crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
name, pcmk_rc_str(rc), rc);
}
return rc;
}
int
pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
{
int rc = pcmk_rc_ok;
xmlNode *request = NULL;
const char *display_host = (node ? node : "localhost");
const char *target = pcmk__node_attr_target(node);
if (target != NULL) {
node = target;
}
request = create_attrd_op(NULL);
crm_xml_add(request, PCMK__XA_TASK, PCMK__ATTRD_CMD_REFRESH);
crm_xml_add(request, PCMK__XA_ATTR_NODE_NAME, node);
if (api == NULL) {
rc = create_api(&api);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = connect_and_send_attrd_request(api, request);
destroy_api(api);
+
+ } else if (!pcmk_ipc_is_connected(api)) {
+ rc = connect_and_send_attrd_request(api, request);
+
} else {
rc = send_attrd_request(api, request);
}
free_xml(request);
crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
display_host, pcmk_rc_str(rc), rc);
return rc;
}
static void
add_op_attr(xmlNode *op, uint32_t options)
{
if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_BOTH);
} else if (pcmk_is_set(options, pcmk__node_attr_value)) {
crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
} else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
crm_xml_add(op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE_DELAY);
}
}
static void
populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
const char *dampen, const char *set, uint32_t options)
{
if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
crm_xml_add(op, PCMK__XA_ATTR_PATTERN, name);
} else {
crm_xml_add(op, PCMK__XA_ATTR_NAME, name);
}
add_op_attr(op, options);
crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
crm_xml_add(op, PCMK__XA_ATTR_DAMPENING, dampen);
crm_xml_add(op, PCMK__XA_ATTR_NODE_NAME, node);
crm_xml_add(op, PCMK__XA_ATTR_SET, set);
crm_xml_add_int(op, PCMK__XA_ATTR_IS_REMOTE,
pcmk_is_set(options, pcmk__node_attr_remote));
crm_xml_add_int(op, PCMK__XA_ATTR_IS_PRIVATE,
pcmk_is_set(options, pcmk__node_attr_private));
}
int
pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
const char *value, const char *dampen, const char *set,
const char *user_name, uint32_t options)
{
int rc = pcmk_rc_ok;
xmlNode *request = NULL;
const char *display_host = (node ? node : "localhost");
const char *target = NULL;
if (name == NULL) {
return EINVAL;
}
target = pcmk__node_attr_target(node);
if (target != NULL) {
node = target;
}
request = create_attrd_op(user_name);
populate_update_op(request, node, name, value, dampen, set, options);
if (api == NULL) {
rc = create_api(&api);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = connect_and_send_attrd_request(api, request);
destroy_api(api);
+
+ } else if (!pcmk_ipc_is_connected(api)) {
+ rc = connect_and_send_attrd_request(api, request);
+
} else {
rc = send_attrd_request(api, request);
}
free_xml(request);
crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
name, display_host, pcmk_rc_str(rc), rc);
return rc;
}
int
pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
const char *set, const char *user_name,
uint32_t options)
{
int rc = pcmk_rc_ok;
xmlNode *request = NULL;
if (attrs == NULL) {
return EINVAL;
}
/* There are two different ways of handling a list of attributes:
*
* (1) For messages originating from some command line tool, we have to send
* them one at a time. In this loop, we just call pcmk__attrd_api_update
* for each, letting it deal with creating the API object if it doesn't
* already exist.
*
* The reason we can't use a single message in this case is that we can't
* trust that the server supports it. Remote nodes could be involved
* here, and there's no guarantee that a newer client running on a remote
* node is talking to (or proxied through) a cluster node with a newer
* attrd. We also can't just try sending a single message and then falling
* back on multiple. There's no handshake with the attrd server to
* determine its version. And then we would need to do that fallback in the
* dispatch function for this to work for all connection types (mainloop in
* particular), and at that point we won't know what the original message
* was in order to break it apart and resend as individual messages.
*
* (2) For messages between daemons, we can be assured that the local attrd
* will support the new message and that it can send to the other attrds
* as one request or split up according to the minimum supported version.
*/
for (GList *iter = attrs; iter != NULL; iter = iter->next) {
pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
if (pcmk__is_daemon) {
const char *target = NULL;
xmlNode *child = NULL;
/* First time through this loop - create the basic request. */
if (request == NULL) {
request = create_attrd_op(user_name);
add_op_attr(request, options);
}
/* Add a child node for this operation. We add the task to the top
* level XML node so attrd_ipc_dispatch doesn't need changes. And
* then we also add the task to each child node in populate_update_op
* so attrd_client_update knows what form of update is taking place.
*/
child = create_xml_node(request, XML_ATTR_OP);
target = pcmk__node_attr_target(pair->node);
if (target != NULL) {
pair->node = target;
}
populate_update_op(child, pair->node, pair->name, pair->value, dampen,
set, options);
} else {
rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
dampen, set, user_name, options);
}
}
/* If we were doing multiple attributes at once, we still need to send the
* request. Do that now, creating and destroying the API object if needed.
*/
if (pcmk__is_daemon) {
bool created_api = false;
if (api == NULL) {
rc = create_api(&api);
if (rc != pcmk_rc_ok) {
return rc;
}
created_api = true;
}
rc = connect_and_send_attrd_request(api, request);
free_xml(request);
if (created_api) {
destroy_api(api);
}
}
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Apr 21, 11:16 AM (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1658353
Default Alt Text
(58 KB)

Event Timeline