summaryrefslogtreecommitdiffstats
path: root/daemons/attrd/attrd_cib.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
commite5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch)
treea6716c9275b4b413f6c9194798b34b91affb3cc7 /daemons/attrd/attrd_cib.c
parentInitial commit. (diff)
downloadpacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz
pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'daemons/attrd/attrd_cib.c')
-rw-r--r--daemons/attrd/attrd_cib.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c
new file mode 100644
index 0000000..928c013
--- /dev/null
+++ b/daemons/attrd/attrd_cib.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2013-2023 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 <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include <crm/msg_xml.h>
+#include <crm/common/logging.h>
+#include <crm/common/results.h>
+#include <crm/common/strings_internal.h>
+#include <crm/common/xml.h>
+
+#include "pacemaker-attrd.h"
+
+static int last_cib_op_done = 0;
+
+static gboolean
+attribute_timer_cb(gpointer data)
+{
+ attribute_t *a = data;
+ crm_trace("Dampen interval expired for %s", a->id);
+ attrd_write_or_elect_attribute(a);
+ return FALSE;
+}
+
+static void
+attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data)
+{
+ int level = LOG_ERR;
+ GHashTableIter iter;
+ const char *peer = NULL;
+ attribute_value_t *v = NULL;
+
+ char *name = user_data;
+ attribute_t *a = g_hash_table_lookup(attributes, name);
+
+ if(a == NULL) {
+ crm_info("Attribute %s no longer exists", name);
+ return;
+ }
+
+ a->update = 0;
+ if (rc == pcmk_ok && call_id < 0) {
+ rc = call_id;
+ }
+
+ switch (rc) {
+ case pcmk_ok:
+ level = LOG_INFO;
+ last_cib_op_done = call_id;
+ if (a->timer && !a->timeout_ms) {
+ // Remove temporary dampening for failed writes
+ mainloop_timer_del(a->timer);
+ a->timer = NULL;
+ }
+ break;
+
+ case -pcmk_err_diff_failed: /* When an attr changes while the CIB is syncing */
+ case -ETIME: /* When an attr changes while there is a DC election */
+ case -ENXIO: /* When an attr changes while the CIB is syncing a
+ * newer config from a node that just came up
+ */
+ level = LOG_WARNING;
+ break;
+ }
+
+ do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
+ call_id, a->id, pcmk_strerror(rc), rc);
+
+ g_hash_table_iter_init(&iter, a->values);
+ while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
+ do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
+ free(v->requested);
+ v->requested = NULL;
+ if (rc != pcmk_ok) {
+ a->changed = true; /* Attempt write out again */
+ }
+ }
+
+ if (a->changed && attrd_election_won()) {
+ if (rc == pcmk_ok) {
+ /* 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);
+
+ /* We're re-attempting a write because the original failed; delay
+ * the next attempt so we don't potentially flood the CIB manager
+ * and logs with a zillion attempts per second.
+ *
+ * @TODO We could elect a new writer instead. However, we'd have to
+ * somehow downgrade our vote, and we'd still need something like this
+ * if all peers similarly fail to write this attribute (which may
+ * indicate a corrupted attribute entry rather than a CIB issue).
+ */
+ } else if (a->timer) {
+ // Attribute has a dampening value, so use that as delay
+ if (!mainloop_timer_running(a->timer)) {
+ crm_trace("Delayed re-attempted write for %s by %s",
+ name, pcmk__readable_interval(a->timeout_ms));
+ mainloop_timer_start(a->timer);
+ }
+ } else {
+ /* Set a temporary dampening of 2 seconds (timer will continue
+ * to exist until the attribute's dampening gets set or the
+ * write succeeds).
+ */
+ a->timer = attrd_add_timer(a->id, 2000, a);
+ mainloop_timer_start(a->timer);
+ }
+ }
+}
+
+static void
+build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
+{
+ const char *set = NULL;
+ xmlNode *xml_obj = NULL;
+
+ xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
+ crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
+
+ xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
+ crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
+
+ 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);
+ }
+
+ 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);
+ }
+ set = ID(xml_obj);
+
+ xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
+ if (a->uuid) {
+ crm_xml_set_id(xml_obj, "%s", a->uuid);
+ } else {
+ crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
+ }
+ crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
+
+ if(value) {
+ crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
+
+ } else {
+ crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
+ crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
+ }
+}
+
+static void
+send_alert_attributes_value(attribute_t *a, GHashTable *t)
+{
+ int rc = 0;
+ attribute_value_t *at = NULL;
+ GHashTableIter vIter;
+
+ g_hash_table_iter_init(&vIter, t);
+
+ while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
+ rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
+ a->id, at->current);
+ crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
+ a->id, at->nodename, at->current, at->nodeid, rc);
+ }
+}
+
+static void
+set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
+{
+ attribute_value_t *a_v = NULL;
+ a_v = calloc(1, sizeof(attribute_value_t));
+ CRM_ASSERT(a_v != NULL);
+
+ a_v->nodeid = v->nodeid;
+ a_v->nodename = strdup(v->nodename);
+ pcmk__str_update(&a_v->current, v->current);
+
+ g_hash_table_replace(t, a_v->nodename, a_v);
+}
+
+mainloop_timer_t *
+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)
+{
+ 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;
+
+ if (a == NULL) {
+ return;
+ }
+
+ /* 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);
+ 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;
+
+ } 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);
+ } else {
+ crm_info("Write out of '%s' delayed: timer is running", a->id);
+ return;
+ }
+ }
+
+ /* Initialize the status update XML */
+ xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ }
+
+ /* Attribute will be written shortly, so clear changed flag */
+ a->changed = false;
+
+ /* We will check all peers' uuids shortly, so initialize this to false */
+ a->unknown_peer_uuids = false;
+
+ /* Attribute will be written shortly, so clear forced write flag */
+ a->force_write = FALSE;
+
+ /* Make the table for the attribute trap */
+ 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);
+
+ /* If the value's peer info does not correspond to a peer, ignore it */
+ if (peer == NULL) {
+ crm_notice("Cannot update %s[%s]=%s because peer not known",
+ a->id, v->nodename, v->current);
+ continue;
+ }
+
+ /* If we're just learning the peer's node id, remember it */
+ if (peer->id && (v->nodeid == 0)) {
+ crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
+ v->nodeid = peer->id;
+ }
+
+ /* If this is a private attribute, no update needs to be sent */
+ if (stand_alone || a->is_private) {
+ private_updates++;
+ continue;
+ }
+
+ /* If the peer is found, but its uuid is unknown, defer write */
+ if (peer->uuid == NULL) {
+ a->unknown_peer_uuids = true;
+ crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
+ "(will retry if learned)",
+ a->id, v->nodename, v->current);
+ continue;
+ }
+
+ /* Add this value to status update XML */
+ crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID %u/%u)",
+ 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 */
+ set_alert_attribute_value(alert_attribute_value, v);
+
+ free(v->requested);
+ 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);
+ }
+ }
+
+ if (private_updates) {
+ crm_info("Processed %d private change%s for %s, id=%s, set=%s",
+ 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__);
+
+ a->update = cib_internal_op(the_cib, PCMK__CIB_REQUEST_MODIFY, NULL,
+ XML_CIB_TAG_STATUS, xml_top, NULL, flags,
+ a->user);
+
+ 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);
+ }
+
+ g_hash_table_destroy(alert_attribute_value);
+ free_xml(xml_top);
+}
+
+void
+attrd_write_attributes(bool all, bool ignore_delay)
+{
+ GHashTableIter iter;
+ attribute_t *a = NULL;
+
+ crm_debug("Writing out %s attributes", 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) {
+ // Try writing this attribute again, in case peer ID was learned
+ a->changed = true;
+ } else if (a->force_write) {
+ /* If the force_write flag is set, write the attribute. */
+ 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));
+ } else {
+ crm_trace("Skipping unchanged attribute %s", a->id);
+ }
+ }
+}
+
+void
+attrd_write_or_elect_attribute(attribute_t *a)
+{
+ if (attrd_election_won()) {
+ attrd_write_attribute(a, false);
+ } else {
+ attrd_start_election_if_needed();
+ }
+}