summaryrefslogtreecommitdiffstats
path: root/daemons/attrd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 07:45:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 07:45:52 +0000
commitcbe4cdc48486fbedede448aa59aa6a367efa1038 (patch)
tree6fc30a23fe7642fd93c5c6c702c4bc8ed5640b38 /daemons/attrd
parentAdding debian version 2.1.6-5. (diff)
downloadpacemaker-cbe4cdc48486fbedede448aa59aa6a367efa1038.tar.xz
pacemaker-cbe4cdc48486fbedede448aa59aa6a367efa1038.zip
Merging upstream version 2.1.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--daemons/attrd/Makefile.am43
-rw-r--r--daemons/attrd/attrd_alerts.c21
-rw-r--r--daemons/attrd/attrd_attributes.c46
-rw-r--r--daemons/attrd/attrd_cib.c464
-rw-r--r--daemons/attrd/attrd_corosync.c21
-rw-r--r--daemons/attrd/attrd_elections.c14
-rw-r--r--daemons/attrd/attrd_ipc.c25
-rw-r--r--daemons/attrd/attrd_messages.c34
-rw-r--r--daemons/attrd/attrd_sync.c4
-rw-r--r--daemons/attrd/attrd_utils.c59
-rw-r--r--daemons/attrd/pacemaker-attrd.c140
-rw-r--r--daemons/attrd/pacemaker-attrd.h17
12 files changed, 541 insertions, 347 deletions
diff --git a/daemons/attrd/Makefile.am b/daemons/attrd/Makefile.am
index 6bb81c4..f8d8bc9 100644
--- a/daemons/attrd/Makefile.am
+++ b/daemons/attrd/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2004-2022 the Pacemaker project contributors
+# Copyright 2004-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -18,31 +18,32 @@ noinst_HEADERS = pacemaker-attrd.h
pacemaker_attrd_CFLAGS = $(CFLAGS_HARDENED_EXE)
pacemaker_attrd_LDFLAGS = $(LDFLAGS_HARDENED_EXE)
-pacemaker_attrd_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la \
- $(top_builddir)/lib/pengine/libpe_rules.la \
- $(top_builddir)/lib/common/libcrmcommon.la \
- $(top_builddir)/lib/cib/libcib.la \
- $(top_builddir)/lib/lrmd/liblrmd.la \
- $(CLUSTERLIBS)
+pacemaker_attrd_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la
+pacemaker_attrd_LDADD += $(top_builddir)/lib/cib/libcib.la
+pacemaker_attrd_LDADD += $(top_builddir)/lib/pengine/libpe_rules.la
+pacemaker_attrd_LDADD += $(top_builddir)/lib/lrmd/liblrmd.la
+pacemaker_attrd_LDADD += $(top_builddir)/lib/common/libcrmcommon.la
+pacemaker_attrd_LDADD += $(CLUSTERLIBS)
pacemaker_attrd_SOURCES = attrd_alerts.c \
- attrd_attributes.c \
- attrd_cib.c \
- attrd_corosync.c \
- attrd_elections.c \
- attrd_ipc.c \
- attrd_messages.c \
- attrd_sync.c \
- attrd_utils.c \
- pacemaker-attrd.c
-
-clean-generic:
- rm -f *.log *.debug *.xml *~
-
-if BUILD_LEGACY_LINKS
+ attrd_attributes.c \
+ attrd_cib.c \
+ attrd_corosync.c \
+ attrd_elections.c \
+ attrd_ipc.c \
+ attrd_messages.c \
+ attrd_sync.c \
+ attrd_utils.c \
+ pacemaker-attrd.c
+
+.PHONY: install-exec-hook
install-exec-hook:
+if BUILD_LEGACY_LINKS
cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f attrd && $(LN_S) pacemaker-attrd attrd
+endif
+.PHONY: uninstall-hook
uninstall-hook:
+if BUILD_LEGACY_LINKS
cd $(DESTDIR)$(CRM_DAEMON_DIR) && rm -f attrd
endif
diff --git a/daemons/attrd/attrd_alerts.c b/daemons/attrd/attrd_alerts.c
index b694891..495e18f 100644
--- a/daemons/attrd/attrd_alerts.c
+++ b/daemons/attrd/attrd_alerts.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2021 the Pacemaker project contributors
+ * Copyright 2015-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,6 +14,7 @@
#include <crm/cluster/internal.h>
#include <crm/cluster/election_internal.h>
#include <crm/common/alerts_internal.h>
+#include <crm/common/cib_internal.h>
#include <crm/pengine/rules_internal.h>
#include <crm/lrmd_internal.h>
#include "pacemaker-attrd.h"
@@ -92,7 +93,7 @@ config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
}
crmalerts = output;
- if (crmalerts && !pcmk__str_eq(crm_element_name(crmalerts), XML_CIB_TAG_ALERTS, pcmk__str_none)) {
+ if ((crmalerts != NULL) && !pcmk__xe_is(crmalerts, XML_CIB_TAG_ALERTS)) {
crmalerts = first_named_child(crmalerts, XML_CIB_TAG_ALERTS);
}
if (!crmalerts) {
@@ -104,9 +105,6 @@ config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
attrd_alert_list = pe_unpack_alerts(crmalerts);
}
-#define XPATH_ALERTS \
- "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_ALERTS
-
gboolean
attrd_read_options(gpointer user_data)
{
@@ -114,8 +112,9 @@ attrd_read_options(gpointer user_data)
CRM_CHECK(the_cib != NULL, return TRUE);
- call_id = the_cib->cmds->query(the_cib, XPATH_ALERTS, NULL,
- cib_xpath | cib_scope_local);
+ call_id = the_cib->cmds->query(the_cib,
+ pcmk__cib_abs_xpath_for(XML_CIB_TAG_ALERTS),
+ NULL, cib_xpath|cib_scope_local);
the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, NULL,
"config_query_callback",
@@ -125,14 +124,6 @@ attrd_read_options(gpointer user_data)
return TRUE;
}
-void
-attrd_cib_updated_cb(const char *event, xmlNode * msg)
-{
- if (!attrd_shutting_down() && pcmk__alert_in_patchset(msg, false)) {
- mainloop_set_trigger(attrd_config_read);
- }
-}
-
int
attrd_send_attribute_alert(const char *node, int nodeid,
const char *attr, const char *value)
diff --git a/daemons/attrd/attrd_attributes.c b/daemons/attrd/attrd_attributes.c
index 516ced7..388c181 100644
--- a/daemons/attrd/attrd_attributes.c
+++ b/daemons/attrd/attrd_attributes.c
@@ -25,25 +25,45 @@
static attribute_t *
attrd_create_attribute(xmlNode *xml)
{
+ int is_private = 0;
int dampen = 0;
- const char *value = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
- attribute_t *a = calloc(1, sizeof(attribute_t));
+ const char *name = crm_element_value(xml, PCMK__XA_ATTR_NAME);
+ const char *set_type = crm_element_value(xml, PCMK__XA_ATTR_SET_TYPE);
+ const char *dampen_s = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
+ attribute_t *a = NULL;
+
+ if (set_type == NULL) {
+ set_type = XML_TAG_ATTR_SETS;
+ }
+
+ /* Set type is meaningful only when writing to the CIB. Private
+ * attributes are not written.
+ */
+ crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &is_private);
+ if ((is_private != 0)
+ && !pcmk__str_any_of(set_type, XML_TAG_ATTR_SETS, XML_TAG_UTILIZATION,
+ NULL)) {
+ crm_warn("Ignoring attribute %s with invalid set type %s",
+ pcmk__s(name, "(unidentified)"), set_type);
+ return NULL;
+ }
+ a = calloc(1, sizeof(attribute_t));
CRM_ASSERT(a != NULL);
- a->id = crm_element_value_copy(xml, PCMK__XA_ATTR_NAME);
- a->set_id = crm_element_value_copy(xml, PCMK__XA_ATTR_SET);
- a->set_type = crm_element_value_copy(xml, PCMK__XA_ATTR_SET_TYPE);
- a->uuid = crm_element_value_copy(xml, PCMK__XA_ATTR_UUID);
- a->values = pcmk__strikey_table(NULL, attrd_free_attribute_value);
+ a->is_private = is_private;
+ pcmk__str_update(&a->id, name);
+ pcmk__str_update(&a->set_type, set_type);
- crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &a->is_private);
+ a->set_id = crm_element_value_copy(xml, PCMK__XA_ATTR_SET);
+ a->uuid = crm_element_value_copy(xml, PCMK__XA_ATTR_UUID);
+ a->values = pcmk__strikey_table(NULL, attrd_free_attribute_value);
a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER);
crm_trace("Performing all %s operations as user '%s'", a->id, a->user);
- if (value != NULL) {
- dampen = crm_get_msec(value);
+ if (dampen_s != NULL) {
+ dampen = crm_get_msec(dampen_s);
}
crm_trace("Created attribute %s with %s write delay", a->id,
(a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
@@ -52,7 +72,7 @@ attrd_create_attribute(xmlNode *xml)
a->timeout_ms = dampen;
a->timer = attrd_add_timer(a->id, a->timeout_ms, a);
} else if (dampen < 0) {
- crm_warn("Ignoring invalid delay %s for attribute %s", value, a->id);
+ crm_warn("Ignoring invalid delay %s for attribute %s", dampen_s, a->id);
}
g_hash_table_replace(attributes, a->id, a);
@@ -169,6 +189,10 @@ attrd_populate_attribute(xmlNode *xml, const char *attr)
if (a == NULL) {
if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_none)) {
a = attrd_create_attribute(xml);
+ if (a == NULL) {
+ return NULL;
+ }
+
} else {
crm_warn("Could not update %s: attribute not found", attr);
return NULL;
diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c
index 928c013..80e5580 100644
--- a/daemons/attrd/attrd_cib.c
+++ b/daemons/attrd/attrd_cib.c
@@ -10,6 +10,7 @@
#include <crm_internal.h>
#include <errno.h>
+#include <inttypes.h> // PRIu32
#include <stdbool.h>
#include <stdlib.h>
#include <glib.h>
@@ -24,6 +25,188 @@
static int last_cib_op_done = 0;
+static void write_attribute(attribute_t *a, bool ignore_delay);
+
+static void
+attrd_cib_destroy_cb(gpointer user_data)
+{
+ cib_t *cib = user_data;
+
+ cib->cmds->signoff(cib);
+
+ if (attrd_shutting_down(false)) {
+ crm_info("Disconnected from the CIB manager");
+
+ } else {
+ // @TODO This should trigger a reconnect, not a shutdown
+ crm_crit("Lost connection to the CIB manager, shutting down");
+ attrd_exit_status = CRM_EX_DISCONNECT;
+ attrd_shutdown(0);
+ }
+}
+
+static void
+attrd_cib_updated_cb(const char *event, xmlNode *msg)
+{
+ const xmlNode *patchset = NULL;
+ const char *client_name = NULL;
+
+ if (attrd_shutting_down(true)) {
+ return;
+ }
+
+ if (cib__get_notify_patchset(msg, &patchset) != pcmk_rc_ok) {
+ return;
+ }
+
+ if (cib__element_in_patchset(patchset, XML_CIB_TAG_ALERTS)) {
+ mainloop_set_trigger(attrd_config_read);
+ }
+
+ if (!attrd_election_won()) {
+ // Don't write attributes if we're not the writer
+ return;
+ }
+
+ client_name = crm_element_value(msg, F_CIB_CLIENTNAME);
+ if (!cib__client_triggers_refresh(client_name)) {
+ // The CIB is still accurate
+ return;
+ }
+
+ if (cib__element_in_patchset(patchset, XML_CIB_TAG_NODES)
+ || cib__element_in_patchset(patchset, XML_CIB_TAG_STATUS)) {
+
+ /* An unsafe client modified the nodes or status section. Write
+ * transient attributes to ensure they're up-to-date in the CIB.
+ */
+ if (client_name == NULL) {
+ client_name = crm_element_value(msg, F_CIB_CLIENTID);
+ }
+ crm_notice("Updating all attributes after %s event triggered by %s",
+ event, pcmk__s(client_name, "(unidentified client)"));
+
+ attrd_write_attributes(attrd_write_all);
+ }
+}
+
+int
+attrd_cib_connect(int max_retry)
+{
+ static int attempts = 0;
+
+ int rc = -ENOTCONN;
+
+ the_cib = cib_new();
+ if (the_cib == NULL) {
+ return -ENOTCONN;
+ }
+
+ do {
+ if (attempts > 0) {
+ sleep(attempts);
+ }
+ attempts++;
+ crm_debug("Connection attempt %d to the CIB manager", attempts);
+ rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
+
+ } while ((rc != pcmk_ok) && (attempts < max_retry));
+
+ if (rc != pcmk_ok) {
+ crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
+ pcmk_strerror(rc), rc);
+ goto cleanup;
+ }
+
+ crm_debug("Connected to the CIB manager after %d attempts", attempts);
+
+ rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
+ if (rc != pcmk_ok) {
+ crm_err("Could not set disconnection callback");
+ goto cleanup;
+ }
+
+ rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY,
+ attrd_cib_updated_cb);
+ if (rc != pcmk_ok) {
+ crm_err("Could not set CIB notification callback");
+ goto cleanup;
+ }
+
+ return pcmk_ok;
+
+cleanup:
+ cib__clean_up_connection(&the_cib);
+ return -ENOTCONN;
+}
+
+void
+attrd_cib_disconnect(void)
+{
+ CRM_CHECK(the_cib != NULL, return);
+ the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY,
+ attrd_cib_updated_cb);
+ cib__clean_up_connection(&the_cib);
+}
+
+static void
+attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
+ void *user_data)
+{
+ do_crm_log_unlikely(((rc != pcmk_ok)? LOG_NOTICE : LOG_DEBUG),
+ "Cleared transient attributes: %s "
+ CRM_XS " xpath=%s rc=%d",
+ pcmk_strerror(rc), (char *) user_data, rc);
+}
+
+#define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
+
+/*!
+ * \internal
+ * \brief Wipe all transient attributes for this node from the CIB
+ *
+ * Clear any previous transient node attributes from the CIB. This is
+ * normally done by the DC's controller when this node leaves the cluster, but
+ * this handles the case where the node restarted so quickly that the
+ * cluster layer didn't notice.
+ *
+ * \todo If pacemaker-attrd respawns after crashing (see PCMK_ENV_RESPAWNED),
+ * ideally we'd skip this and sync our attributes from the writer.
+ * However, currently we reject any values for us that the writer has, in
+ * attrd_peer_update().
+ */
+static void
+attrd_erase_attrs(void)
+{
+ int call_id = 0;
+ char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
+
+ crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
+ xpath);
+
+ call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath);
+ the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
+ "attrd_erase_cb", attrd_erase_cb,
+ free);
+}
+
+/*!
+ * \internal
+ * \brief Prepare the CIB after cluster is connected
+ */
+void
+attrd_cib_init(void)
+{
+ // We have no attribute values in memory, wipe the CIB to match
+ attrd_erase_attrs();
+
+ // Set a trigger for reading the CIB (for the alerts section)
+ attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
+
+ // Always read the CIB at start-up
+ mainloop_set_trigger(attrd_config_read);
+}
+
static gboolean
attribute_timer_cb(gpointer data)
{
@@ -92,7 +275,7 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use
/* We deferred a write of a new update because this update was in
* progress. Write out the new value without additional delay.
*/
- attrd_write_attribute(a, false);
+ write_attribute(a, false);
/* We're re-attempting a write because the original failed; delay
* the next attempt so we don't potentially flood the CIB manager
@@ -121,48 +304,134 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use
}
}
-static void
-build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
+/*!
+ * \internal
+ * \brief Add a set-attribute update request to the current CIB transaction
+ *
+ * \param[in] attr Attribute to update
+ * \param[in] attr_id ID of attribute to update
+ * \param[in] node_id ID of node for which to update attribute value
+ * \param[in] set_id ID of attribute set
+ * \param[in] value New value for attribute
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+add_set_attr_update(const attribute_t *attr, const char *attr_id,
+ const char *node_id, const char *set_id, const char *value)
{
- const char *set = NULL;
- xmlNode *xml_obj = NULL;
+ xmlNode *update = create_xml_node(NULL, XML_CIB_TAG_STATE);
+ xmlNode *child = update;
+ int rc = ENOMEM;
- xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
- crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
+ if (child == NULL) {
+ goto done;
+ }
+ crm_xml_add(child, XML_ATTR_ID, node_id);
- xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
- crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
+ child = create_xml_node(child, XML_TAG_TRANSIENT_NODEATTRS);
+ if (child == NULL) {
+ goto done;
+ }
+ crm_xml_add(child, XML_ATTR_ID, node_id);
- if (pcmk__str_eq(a->set_type, XML_TAG_ATTR_SETS, pcmk__str_null_matches)) {
- xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
- } else if (pcmk__str_eq(a->set_type, XML_TAG_UTILIZATION, pcmk__str_none)) {
- xml_obj = create_xml_node(xml_obj, XML_TAG_UTILIZATION);
- } else {
- crm_err("Unknown set type attribute: %s", a->set_type);
+ child = create_xml_node(child, attr->set_type);
+ if (child == NULL) {
+ goto done;
}
+ crm_xml_add(child, XML_ATTR_ID, set_id);
- if (a->set_id) {
- crm_xml_set_id(xml_obj, "%s", a->set_id);
- } else {
- crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
+ child = create_xml_node(child, XML_CIB_TAG_NVPAIR);
+ if (child == NULL) {
+ goto done;
}
- set = ID(xml_obj);
+ crm_xml_add(child, XML_ATTR_ID, attr_id);
+ crm_xml_add(child, XML_NVPAIR_ATTR_NAME, attr->id);
+ crm_xml_add(child, XML_NVPAIR_ATTR_VALUE, value);
+
+ rc = the_cib->cmds->modify(the_cib, XML_CIB_TAG_STATUS, update,
+ cib_can_create|cib_transaction);
+ rc = pcmk_legacy2rc(rc);
+
+done:
+ free_xml(update);
+ return rc;
+}
+
+/*!
+ * \internal
+ * \brief Add an unset-attribute update request to the current CIB transaction
+ *
+ * \param[in] attr Attribute to update
+ * \param[in] attr_id ID of attribute to update
+ * \param[in] node_id ID of node for which to update attribute value
+ * \param[in] set_id ID of attribute set
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+add_unset_attr_update(const attribute_t *attr, const char *attr_id,
+ const char *node_id, const char *set_id)
+{
+ char *xpath = crm_strdup_printf("/" XML_TAG_CIB
+ "/" XML_CIB_TAG_STATUS
+ "/" XML_CIB_TAG_STATE
+ "[@" XML_ATTR_ID "='%s']"
+ "/" XML_TAG_TRANSIENT_NODEATTRS
+ "[@" XML_ATTR_ID "='%s']"
+ "/%s[@" XML_ATTR_ID "='%s']"
+ "/" XML_CIB_TAG_NVPAIR
+ "[@" XML_ATTR_ID "='%s' "
+ "and @" XML_NVPAIR_ATTR_NAME "='%s']",
+ node_id, node_id, attr->set_type, set_id,
+ attr_id, attr->id);
+
+ int rc = the_cib->cmds->remove(the_cib, xpath, NULL,
+ cib_xpath|cib_transaction);
+
+ free(xpath);
+ return pcmk_legacy2rc(rc);
+}
+
+/*!
+ * \internal
+ * \brief Add an attribute update request to the current CIB transaction
+ *
+ * \param[in] attr Attribute to update
+ * \param[in] value New value for attribute
+ * \param[in] node_id ID of node for which to update attribute value
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+add_attr_update(const attribute_t *attr, const char *value, const char *node_id)
+{
+ char *set_id = NULL;
+ char *attr_id = NULL;
+ int rc = pcmk_rc_ok;
- xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
- if (a->uuid) {
- crm_xml_set_id(xml_obj, "%s", a->uuid);
+ if (attr->set_id != NULL) {
+ pcmk__str_update(&set_id, attr->set_id);
} else {
- crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
+ set_id = crm_strdup_printf("%s-%s", XML_CIB_TAG_STATUS, node_id);
}
- crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
+ crm_xml_sanitize_id(set_id);
- if(value) {
- crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
+ if (attr->uuid != NULL) {
+ pcmk__str_update(&attr_id, attr->uuid);
+ } else {
+ attr_id = crm_strdup_printf("%s-%s", set_id, attr->id);
+ }
+ crm_xml_sanitize_id(attr_id);
+ if (value != NULL) {
+ rc = add_set_attr_update(attr, attr_id, node_id, set_id, value);
} else {
- crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
- crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
+ rc = add_unset_attr_update(attr, attr_id, node_id, set_id);
}
+ free(set_id);
+ free(attr_id);
+ return rc;
}
static void
@@ -202,15 +471,22 @@ attrd_add_timer(const char *id, int timeout_ms, attribute_t *attr)
return mainloop_timer_add(id, timeout_ms, FALSE, attribute_timer_cb, attr);
}
-void
-attrd_write_attribute(attribute_t *a, bool ignore_delay)
+/*!
+ * \internal
+ * \brief Write an attribute's values to the CIB if appropriate
+ *
+ * \param[in,out] a Attribute to write
+ * \param[in] ignore_delay If true, write attribute now regardless of any
+ * configured delay
+ */
+static void
+write_attribute(attribute_t *a, bool ignore_delay)
{
int private_updates = 0, cib_updates = 0;
- xmlNode *xml_top = NULL;
attribute_value_t *v = NULL;
GHashTableIter iter;
- enum cib_call_options flags = cib_none;
GHashTable *alert_attribute_value = NULL;
+ int rc = pcmk_ok;
if (a == NULL) {
return;
@@ -218,32 +494,37 @@ attrd_write_attribute(attribute_t *a, bool ignore_delay)
/* If this attribute will be written to the CIB ... */
if (!stand_alone && !a->is_private) {
-
/* Defer the write if now's not a good time */
- CRM_CHECK(the_cib != NULL, return);
if (a->update && (a->update < last_cib_op_done)) {
- crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
+ crm_info("Write out of '%s' continuing: update %d considered lost",
+ a->id, a->update);
a->update = 0; // Don't log this message again
} else if (a->update) {
- crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
- return;
+ crm_info("Write out of '%s' delayed: update %d in progress",
+ a->id, a->update);
+ goto done;
} else if (mainloop_timer_running(a->timer)) {
if (ignore_delay) {
- /* 'refresh' forces a write of the current value of all attributes
- * Cancel any existing timers, we're writing it NOW
- */
mainloop_timer_stop(a->timer);
- crm_debug("Write out of '%s': timer is running but ignore delay", a->id);
+ crm_debug("Overriding '%s' write delay", a->id);
} else {
- crm_info("Write out of '%s' delayed: timer is running", a->id);
- return;
+ crm_info("Delaying write of '%s'", a->id);
+ goto done;
}
}
- /* Initialize the status update XML */
- xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ // Initiate a transaction for all the peer value updates
+ CRM_CHECK(the_cib != NULL, goto done);
+ the_cib->cmds->set_user(the_cib, a->user);
+ rc = the_cib->cmds->init_transaction(the_cib);
+ if (rc != pcmk_ok) {
+ crm_err("Failed to write %s (id %s, set %s): Could not initiate "
+ "CIB transaction",
+ a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
+ goto done;
+ }
}
/* Attribute will be written shortly, so clear changed flag */
@@ -256,12 +537,14 @@ attrd_write_attribute(attribute_t *a, bool ignore_delay)
a->force_write = FALSE;
/* Make the table for the attribute trap */
- alert_attribute_value = pcmk__strikey_table(NULL, attrd_free_attribute_value);
+ alert_attribute_value = pcmk__strikey_table(NULL,
+ attrd_free_attribute_value);
/* Iterate over each peer value of this attribute */
g_hash_table_iter_init(&iter, a->values);
- while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
- crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
+ while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
+ crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename,
+ CRM_GET_PEER_ANY);
/* If the value's peer info does not correspond to a peer, ignore it */
if (peer == NULL) {
@@ -291,11 +574,20 @@ attrd_write_attribute(attribute_t *a, bool ignore_delay)
continue;
}
- /* Add this value to status update XML */
- crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID %u/%u)",
+ // Update this value as part of the CIB transaction we're building
+ rc = add_attr_update(a, v->current, peer->uuid);
+ if (rc != pcmk_rc_ok) {
+ crm_err("Failed to update %s[%s]=%s (peer known as %s, UUID %s, "
+ "ID %" PRIu32 "/%" PRIu32 "): %s",
+ a->id, v->nodename, v->current, peer->uname, peer->uuid,
+ peer->id, v->nodeid, pcmk_rc_str(rc));
+ continue;
+ }
+
+ crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID "
+ "%" PRIu32 "/%" PRIu32 ")",
a->id, v->nodename, v->current,
peer->uname, peer->uuid, peer->id, v->nodeid);
- build_update_element(xml_top, a, peer->uuid, v->current);
cib_updates++;
/* Preservation of the attribute to transmit alert */
@@ -305,12 +597,6 @@ attrd_write_attribute(attribute_t *a, bool ignore_delay)
v->requested = NULL;
if (v->current) {
v->requested = strdup(v->current);
- } else {
- /* Older attrd versions don't know about the cib_mixed_update
- * flag so make sure it goes to the local cib which does
- */
- cib__set_call_options(flags, crm_system_name,
- cib_mixed_update|cib_scope_local);
}
}
@@ -319,40 +605,55 @@ attrd_write_attribute(attribute_t *a, bool ignore_delay)
private_updates, pcmk__plural_s(private_updates),
a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
}
- if (cib_updates) {
- crm_log_xml_trace(xml_top, __func__);
+ if (cib_updates > 0) {
+ char *id = NULL;
- a->update = cib_internal_op(the_cib, PCMK__CIB_REQUEST_MODIFY, NULL,
- XML_CIB_TAG_STATUS, xml_top, NULL, flags,
- a->user);
+ // Commit transaction
+ a->update = the_cib->cmds->end_transaction(the_cib, true, cib_none);
crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
a->update, cib_updates, pcmk__plural_s(cib_updates),
a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
- the_cib->cmds->register_callback_full(the_cib, a->update,
- CIB_OP_TIMEOUT_S, FALSE,
- strdup(a->id),
- "attrd_cib_callback",
- attrd_cib_callback, free);
- /* Transmit alert of the attribute */
- send_alert_attributes_value(a, alert_attribute_value);
+ pcmk__str_update(&id, a->id);
+ if (the_cib->cmds->register_callback_full(the_cib, a->update,
+ CIB_OP_TIMEOUT_S, FALSE, id,
+ "attrd_cib_callback",
+ attrd_cib_callback, free)) {
+ // Transmit alert of the attribute
+ send_alert_attributes_value(a, alert_attribute_value);
+ }
}
- g_hash_table_destroy(alert_attribute_value);
- free_xml(xml_top);
+done:
+ // Discard transaction (if any)
+ if (the_cib != NULL) {
+ the_cib->cmds->end_transaction(the_cib, false, cib_none);
+ the_cib->cmds->set_user(the_cib, NULL);
+ }
+
+ if (alert_attribute_value != NULL) {
+ g_hash_table_destroy(alert_attribute_value);
+ }
}
+/*!
+ * \internal
+ * \brief Write out attributes
+ *
+ * \param[in] options Group of enum attrd_write_options
+ */
void
-attrd_write_attributes(bool all, bool ignore_delay)
+attrd_write_attributes(uint32_t options)
{
GHashTableIter iter;
attribute_t *a = NULL;
- crm_debug("Writing out %s attributes", all? "all" : "changed");
+ crm_debug("Writing out %s attributes",
+ pcmk_is_set(options, attrd_write_all)? "all" : "changed");
g_hash_table_iter_init(&iter, attributes);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
- if (!all && a->unknown_peer_uuids) {
+ if (!pcmk_is_set(options, attrd_write_all) && a->unknown_peer_uuids) {
// Try writing this attribute again, in case peer ID was learned
a->changed = true;
} else if (a->force_write) {
@@ -360,9 +661,14 @@ attrd_write_attributes(bool all, bool ignore_delay)
a->changed = true;
}
- if(all || a->changed) {
- /* When forced write flag is set, ignore delay. */
- attrd_write_attribute(a, (a->force_write ? true : ignore_delay));
+ if (pcmk_is_set(options, attrd_write_all) || a->changed) {
+ bool ignore_delay = pcmk_is_set(options, attrd_write_no_delay);
+
+ if (a->force_write) {
+ // Always ignore delay when forced write flag is set
+ ignore_delay = true;
+ }
+ write_attribute(a, ignore_delay);
} else {
crm_trace("Skipping unchanged attribute %s", a->id);
}
@@ -373,7 +679,7 @@ void
attrd_write_or_elect_attribute(attribute_t *a)
{
if (attrd_election_won()) {
- attrd_write_attribute(a, false);
+ write_attribute(a, false);
} else {
attrd_start_election_if_needed();
}
diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c
index ef205e6..86dc67b 100644
--- a/daemons/attrd/attrd_corosync.c
+++ b/daemons/attrd/attrd_corosync.c
@@ -23,8 +23,6 @@
#include "pacemaker-attrd.h"
-extern crm_exit_t attrd_exit_status;
-
static xmlNode *
attrd_confirmation(int callid)
{
@@ -48,7 +46,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
return;
}
- if (attrd_shutting_down()) {
+ if (attrd_shutting_down(false)) {
/* If we're shutting down, we want to continue responding to election
* ops as long as we're a cluster member (because our vote may be
* needed). Ignore all other messages.
@@ -133,11 +131,11 @@ attrd_cpg_dispatch(cpg_handle_t handle,
static void
attrd_cpg_destroy(gpointer unused)
{
- if (attrd_shutting_down()) {
- crm_info("Corosync disconnection complete");
+ if (attrd_shutting_down(false)) {
+ crm_info("Disconnected from Corosync process group");
} else {
- crm_crit("Lost connection to cluster layer, shutting down");
+ crm_crit("Lost connection to Corosync process group, shutting down");
attrd_exit_status = CRM_EX_DISCONNECT;
attrd_shutdown(0);
}
@@ -180,7 +178,7 @@ cache_remote_node(const char *node_name)
/* If we previously assumed this node was an unseen cluster node,
* remove its entry from the cluster peer cache.
*/
- crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
+ crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name, NULL);
if (dup && (dup->uuid == NULL)) {
reap_crm_member(0, node_name);
@@ -285,7 +283,7 @@ record_peer_nodeid(attribute_value_t *v, const char *host)
crm_trace("Learned %s has node id %s", known_peer->uname, known_peer->uuid);
if (attrd_election_won()) {
- attrd_write_attributes(false, false);
+ attrd_write_attributes(attrd_write_changed);
}
}
@@ -476,9 +474,7 @@ attrd_peer_clear_failure(pcmk__request_t *request)
crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
/* Make sure value is not set, so we delete */
- if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
- crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
- }
+ xml_remove_prop(xml, PCMK__XA_ATTR_VALUE);
g_hash_table_iter_init(&iter, attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
@@ -591,7 +587,8 @@ attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host,
{
bool handle_sync_point = false;
- if (xml_has_children(xml)) {
+ CRM_CHECK((peer != NULL) && (xml != NULL), return);
+ if (xml->children != NULL) {
for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL;
child = crm_next_same_xml(child)) {
attrd_copy_xml_attributes(xml, child);
diff --git a/daemons/attrd/attrd_elections.c b/daemons/attrd/attrd_elections.c
index 3b6b55a..82fbe8a 100644
--- a/daemons/attrd/attrd_elections.c
+++ b/daemons/attrd/attrd_elections.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2022 the Pacemaker project contributors
+ * Copyright 2013-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -25,9 +25,11 @@ attrd_election_cb(gpointer user_data)
/* Update the peers after an election */
attrd_peer_sync(NULL, NULL);
- /* Update the CIB after an election */
- attrd_write_attributes(true, false);
- return FALSE;
+ /* After winning an election, update the CIB with the values of all
+ * attributes as the winner knows them.
+ */
+ attrd_write_attributes(attrd_write_all);
+ return G_SOURCE_REMOVE;
}
void
@@ -48,7 +50,7 @@ attrd_start_election_if_needed(void)
{
if ((peer_writer == NULL)
&& (election_state(writer) != election_in_progress)
- && !attrd_shutting_down()) {
+ && !attrd_shutting_down(false)) {
crm_info("Starting an election to determine the writer");
election_vote(writer);
@@ -70,7 +72,7 @@ attrd_handle_election_op(const crm_node_t *peer, xmlNode *xml)
crm_xml_add(xml, F_CRM_HOST_FROM, peer->uname);
// Don't become writer if we're shutting down
- rc = election_count_vote(writer, xml, !attrd_shutting_down());
+ rc = election_count_vote(writer, xml, !attrd_shutting_down(false));
switch(rc) {
case election_start:
diff --git a/daemons/attrd/attrd_ipc.c b/daemons/attrd/attrd_ipc.c
index 9d3dfff..05c4a69 100644
--- a/daemons/attrd/attrd_ipc.c
+++ b/daemons/attrd/attrd_ipc.c
@@ -140,12 +140,8 @@ attrd_client_clear_failure(pcmk__request_t *request)
}
/* Make sure attribute and value are not set, so we delete via regex */
- if (crm_element_value(xml, PCMK__XA_ATTR_NAME)) {
- crm_xml_replace(xml, PCMK__XA_ATTR_NAME, NULL);
- }
- if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
- crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
- }
+ xml_remove_prop(xml, PCMK__XA_ATTR_NAME);
+ xml_remove_prop(xml, PCMK__XA_ATTR_VALUE);
return attrd_client_update(request);
}
@@ -166,7 +162,8 @@ attrd_client_peer_remove(pcmk__request_t *request)
crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, &nodeid);
if (nodeid > 0) {
- crm_node_t *node = pcmk__search_cluster_node_cache(nodeid, NULL);
+ crm_node_t *node = pcmk__search_cluster_node_cache(nodeid, NULL,
+ NULL);
char *host_alloc = NULL;
if (node && node->uname) {
@@ -235,7 +232,7 @@ attrd_client_refresh(pcmk__request_t *request)
crm_info("Updating all attributes");
attrd_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags);
- attrd_write_attributes(true, true);
+ attrd_write_attributes(attrd_write_all|attrd_write_no_delay);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
return NULL;
@@ -282,7 +279,7 @@ expand_regexes(xmlNode *xml, const char *attr, const char *value, const char *re
* regex and replace it with the name.
*/
attrd_copy_xml_attributes(xml, child);
- crm_xml_replace(child, PCMK__XA_ATTR_PATTERN, NULL);
+ xml_remove_prop(child, PCMK__XA_ATTR_PATTERN);
crm_xml_add(child, PCMK__XA_ATTR_NAME, attr);
}
}
@@ -401,14 +398,18 @@ send_child_update(xmlNode *child, void *data)
xmlNode *
attrd_client_update(pcmk__request_t *request)
{
- xmlNode *xml = request->xml;
+ xmlNode *xml = NULL;
const char *attr, *value, *regex;
+ CRM_CHECK((request != NULL) && (request->xml != NULL), return NULL);
+
+ xml = request->xml;
+
/* If the message has children, that means it is a message from a newer
* client that supports sending multiple operations at a time. There are
* two ways we can handle that.
*/
- if (xml_has_children(xml)) {
+ if (xml->children != NULL) {
if (ATTRD_SUPPORTS_MULTI_MESSAGE(minimum_protocol_version)) {
/* First, if all peers support a certain protocol version, we can
* just broadcast the big message and they'll handle it. However,
@@ -494,7 +495,7 @@ static int32_t
attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
crm_trace("New client connection %p", c);
- if (attrd_shutting_down()) {
+ if (attrd_shutting_down(false)) {
crm_info("Ignoring new connection from pid %d during shutdown",
pcmk__client_pid(c));
return -EPERM;
diff --git a/daemons/attrd/attrd_messages.c b/daemons/attrd/attrd_messages.c
index 184176a..89da6d8 100644
--- a/daemons/attrd/attrd_messages.c
+++ b/daemons/attrd/attrd_messages.c
@@ -20,6 +20,36 @@ int minimum_protocol_version = -1;
static GHashTable *attrd_handlers = NULL;
+static bool
+is_sync_point_attr(xmlAttrPtr attr, void *data)
+{
+ return pcmk__str_eq((const char *) attr->name, PCMK__XA_ATTR_SYNC_POINT, pcmk__str_none);
+}
+
+static int
+remove_sync_point_attribute(xmlNode *xml, void *data)
+{
+ pcmk__xe_remove_matching_attrs(xml, is_sync_point_attr, NULL);
+ pcmk__xe_foreach_child(xml, XML_ATTR_OP, remove_sync_point_attribute, NULL);
+ return pcmk_rc_ok;
+}
+
+/* Sync points on a multi-update IPC message to an attrd too old to support
+ * multi-update messages won't work. Strip the sync point attribute off here
+ * so we don't pretend to support this situation and instead ACK the client
+ * immediately.
+ */
+static void
+remove_unsupported_sync_points(pcmk__request_t *request)
+{
+ if (request->xml->children != NULL && !ATTRD_SUPPORTS_MULTI_MESSAGE(minimum_protocol_version) &&
+ attrd_request_has_sync_point(request->xml)) {
+ crm_warn("Ignoring sync point in request from %s because not all nodes support it",
+ pcmk__request_origin(request));
+ remove_sync_point_attribute(request->xml, NULL);
+ }
+}
+
static xmlNode *
handle_unknown_request(pcmk__request_t *request)
{
@@ -42,6 +72,8 @@ handle_clear_failure_request(pcmk__request_t *request)
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
return NULL;
} else {
+ remove_unsupported_sync_points(request);
+
if (attrd_request_has_sync_point(request->xml)) {
/* If this client supplied a sync point it wants to wait for, add it to
* the wait list. Clients on this list will not receive an ACK until
@@ -180,6 +212,8 @@ handle_update_request(pcmk__request_t *request)
return NULL;
} else {
+ remove_unsupported_sync_points(request);
+
if (attrd_request_has_sync_point(request->xml)) {
/* If this client supplied a sync point it wants to wait for, add it to
* the wait list. Clients on this list will not receive an ACK until
diff --git a/daemons/attrd/attrd_sync.c b/daemons/attrd/attrd_sync.c
index d59ddd5..1a6c24c 100644
--- a/daemons/attrd/attrd_sync.c
+++ b/daemons/attrd/attrd_sync.c
@@ -313,7 +313,9 @@ attrd_cluster_sync_point_update(xmlNode *xml)
const char *
attrd_request_sync_point(xmlNode *xml)
{
- if (xml_has_children(xml)) {
+ CRM_CHECK(xml != NULL, return NULL);
+
+ if (xml->children != NULL) {
xmlNode *child = pcmk__xe_match(xml, XML_ATTR_OP, PCMK__XA_ATTR_SYNC_POINT, NULL);
if (child) {
diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c
index 7de8dd9..341ee1a 100644
--- a/daemons/attrd/attrd_utils.c
+++ b/daemons/attrd/attrd_utils.c
@@ -56,26 +56,22 @@ attrd_clear_requesting_shutdown(void)
/*!
* \internal
- * \brief Check whether we're currently requesting shutdown
+ * \brief Check whether local attribute manager is shutting down
*
- * \return true if requesting shutdown, false otherwise
- */
-bool
-attrd_requesting_shutdown(void)
-{
- return requesting_shutdown;
-}
-
-/*!
- * \internal
- * \brief Check whether we're currently shutting down
+ * \param[in] if_requested Also consider presence of "shutdown" attribute
*
- * \return true if shutting down, false otherwise
+ * \return \c true if local attribute manager has begun shutdown sequence
+ * or (if \p if_requested is \c true) whether local node has a nonzero
+ * "shutdown" attribute set, otherwise \c false
+ * \note Most callers should pass \c false for \p if_requested, because the
+ * attribute manager needs to continue performing while the controller is
+ * shutting down, and even needs to be eligible for election in case all
+ * nodes are shutting down.
*/
bool
-attrd_shutting_down(void)
+attrd_shutting_down(bool if_requested)
{
- return shutting_down;
+ return shutting_down || (if_requested && requesting_shutdown);
}
/*!
@@ -137,39 +133,6 @@ attrd_run_mainloop(void)
g_main_loop_run(mloop);
}
-void
-attrd_cib_disconnect(void)
-{
- CRM_CHECK(the_cib != NULL, return);
- the_cib->cmds->del_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
- the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
- cib__clean_up_connection(&the_cib);
-}
-
-void
-attrd_cib_replaced_cb(const char *event, xmlNode * msg)
-{
- int change_section = cib_change_section_nodes | cib_change_section_status | cib_change_section_alerts;
-
- if (attrd_requesting_shutdown() || attrd_shutting_down()) {
- return;
- }
-
- crm_element_value_int(msg, F_CIB_CHANGE_SECTION, &change_section);
-
- if (attrd_election_won()) {
- if (change_section & (cib_change_section_nodes | cib_change_section_status)) {
- crm_notice("Updating all attributes after %s event", event);
- attrd_write_attributes(true, false);
- }
- }
-
- if (change_section & cib_change_section_alerts) {
- // Check for changes in alerts
- mainloop_set_trigger(attrd_config_read);
- }
-}
-
/* strlen("value") */
#define plus_plus_len (5)
diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c
index 037825b..8091c5b 100644
--- a/daemons/attrd/pacemaker-attrd.c
+++ b/daemons/attrd/pacemaker-attrd.c
@@ -63,140 +63,6 @@ crm_cluster_t *attrd_cluster = NULL;
crm_trigger_t *attrd_config_read = NULL;
crm_exit_t attrd_exit_status = CRM_EX_OK;
-static void
-attrd_cib_destroy_cb(gpointer user_data)
-{
- cib_t *conn = user_data;
-
- conn->cmds->signoff(conn); /* Ensure IPC is cleaned up */
-
- if (attrd_shutting_down()) {
- crm_info("Connection disconnection complete");
-
- } else {
- /* eventually this should trigger a reconnect, not a shutdown */
- crm_crit("Lost connection to the CIB manager, shutting down");
- attrd_exit_status = CRM_EX_DISCONNECT;
- attrd_shutdown(0);
- }
-
- return;
-}
-
-static void
-attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
- void *user_data)
-{
- do_crm_log_unlikely((rc? LOG_NOTICE : LOG_DEBUG),
- "Cleared transient attributes: %s "
- CRM_XS " xpath=%s rc=%d",
- pcmk_strerror(rc), (char *) user_data, rc);
-}
-
-#define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
-
-/*!
- * \internal
- * \brief Wipe all transient attributes for this node from the CIB
- *
- * Clear any previous transient node attributes from the CIB. This is
- * normally done by the DC's controller when this node leaves the cluster, but
- * this handles the case where the node restarted so quickly that the
- * cluster layer didn't notice.
- *
- * \todo If pacemaker-attrd respawns after crashing (see PCMK_respawned),
- * ideally we'd skip this and sync our attributes from the writer.
- * However, currently we reject any values for us that the writer has, in
- * attrd_peer_update().
- */
-static void
-attrd_erase_attrs(void)
-{
- int call_id;
- char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
-
- crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
- xpath);
-
- call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath);
- the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
- "attrd_erase_cb", attrd_erase_cb,
- free);
-}
-
-static int
-attrd_cib_connect(int max_retry)
-{
- static int attempts = 0;
-
- int rc = -ENOTCONN;
-
- the_cib = cib_new();
- if (the_cib == NULL) {
- return -ENOTCONN;
- }
-
- do {
- if(attempts > 0) {
- sleep(attempts);
- }
-
- attempts++;
- crm_debug("Connection attempt %d to the CIB manager", attempts);
- rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
-
- } while(rc != pcmk_ok && attempts < max_retry);
-
- if (rc != pcmk_ok) {
- crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
- pcmk_strerror(rc), rc);
- goto cleanup;
- }
-
- crm_debug("Connected to the CIB manager after %d attempts", attempts);
-
- rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
- if (rc != pcmk_ok) {
- crm_err("Could not set disconnection callback");
- goto cleanup;
- }
-
- rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
- if(rc != pcmk_ok) {
- crm_err("Could not set CIB notification callback");
- goto cleanup;
- }
-
- rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
- if (rc != pcmk_ok) {
- crm_err("Could not set CIB notification callback (update)");
- goto cleanup;
- }
-
- return pcmk_ok;
-
- cleanup:
- cib__clean_up_connection(&the_cib);
- return -ENOTCONN;
-}
-
-/*!
- * \internal
- * \brief Prepare the CIB after cluster is connected
- */
-static void
-attrd_cib_init(void)
-{
- // We have no attribute values in memory, wipe the CIB to match
- attrd_erase_attrs();
-
- // Set a trigger for reading the CIB (for the alerts section)
- attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
-
- // Always read the CIB at start-up
- mainloop_set_trigger(attrd_config_read);
-}
-
static bool
ipc_already_running(void)
{
@@ -208,8 +74,10 @@ ipc_already_running(void)
return false;
}
- rc = pcmk_connect_ipc(old_instance, pcmk_ipc_dispatch_sync);
+ rc = pcmk__connect_ipc(old_instance, pcmk_ipc_dispatch_sync, 2);
if (rc != pcmk_rc_ok) {
+ crm_debug("No existing %s manager instance found: %s",
+ pcmk_ipc_name(old_instance, true), pcmk_rc_str(rc));
pcmk_free_ipc_api(old_instance);
return false;
}
@@ -277,7 +145,7 @@ main(int argc, char **argv)
attrd_exit_status = CRM_EX_OK;
g_set_error(&error, PCMK__EXITC_ERROR, attrd_exit_status, "%s", msg);
- crm_err(msg);
+ crm_err("%s", msg);
goto done;
}
diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h
index 329fb5a..b8929a7 100644
--- a/daemons/attrd/pacemaker-attrd.h
+++ b/daemons/attrd/pacemaker-attrd.h
@@ -57,13 +57,14 @@ void attrd_run_mainloop(void);
void attrd_set_requesting_shutdown(void);
void attrd_clear_requesting_shutdown(void);
void attrd_free_waitlist(void);
-bool attrd_requesting_shutdown(void);
-bool attrd_shutting_down(void);
+bool attrd_shutting_down(bool if_requested);
void attrd_shutdown(int nsig);
void attrd_init_ipc(void);
void attrd_ipc_fini(void);
+int attrd_cib_connect(int max_retry);
void attrd_cib_disconnect(void);
+void attrd_cib_init(void);
bool attrd_value_needs_expansion(const char *value);
int attrd_expand_value(const char *value, const char *old_value);
@@ -92,6 +93,7 @@ int attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
guint interval_ms);
extern cib_t *the_cib;
+extern crm_exit_t attrd_exit_status;
/* Alerts */
@@ -100,8 +102,6 @@ extern crm_trigger_t *attrd_config_read;
void attrd_lrmd_disconnect(void);
gboolean attrd_read_options(gpointer user_data);
-void attrd_cib_replaced_cb(const char *event, xmlNode * msg);
-void attrd_cib_updated_cb(const char *event, xmlNode *msg);
int attrd_send_attribute_alert(const char *node, int nodeid,
const char *attr, const char *value);
@@ -177,8 +177,13 @@ void attrd_free_attribute(gpointer data);
void attrd_free_attribute_value(gpointer data);
attribute_t *attrd_populate_attribute(xmlNode *xml, const char *attr);
-void attrd_write_attribute(attribute_t *a, bool ignore_delay);
-void attrd_write_attributes(bool all, bool ignore_delay);
+enum attrd_write_options {
+ attrd_write_changed = 0,
+ attrd_write_all = (1 << 0),
+ attrd_write_no_delay = (1 << 1),
+};
+
+void attrd_write_attributes(uint32_t options);
void attrd_write_or_elect_attribute(attribute_t *a);
extern int minimum_protocol_version;