Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3686522
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
58 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment