summaryrefslogtreecommitdiffstats
path: root/daemons/based/based_messages.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/based/based_messages.c')
-rw-r--r--daemons/based/based_messages.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c
new file mode 100644
index 0000000..d46456c
--- /dev/null
+++ b/daemons/based/based_messages.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2004-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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <crm/crm.h>
+#include <crm/cib/internal.h>
+#include <crm/msg_xml.h>
+
+#include <crm/common/xml.h>
+#include <crm/common/ipc_internal.h>
+#include <crm/common/xml_internal.h>
+#include <crm/cluster/internal.h>
+
+#include <pacemaker-based.h>
+
+/* Maximum number of diffs to ignore while waiting for a resync */
+#define MAX_DIFF_RETRY 5
+
+bool based_is_primary = false;
+
+xmlNode *the_cib = NULL;
+
+int
+cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ const char *host = crm_element_value(req, F_ORIG);
+
+ *answer = NULL;
+
+ if (crm_element_value(req, F_CIB_ISREPLY) == NULL) {
+ crm_info("Peer %s is requesting to shut down", host);
+ return pcmk_ok;
+ }
+
+ if (cib_shutdown_flag == FALSE) {
+ crm_err("Peer %s mistakenly thinks we wanted to shut down", host);
+ return -EINVAL;
+ }
+
+ crm_info("Peer %s has acknowledged our shutdown request", host);
+ terminate_cib(__func__, 0);
+ return pcmk_ok;
+}
+
+int
+cib_process_default(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ int result = pcmk_ok;
+
+ crm_trace("Processing \"%s\" event", op);
+ *answer = NULL;
+
+ if (op == NULL) {
+ result = -EINVAL;
+ crm_err("No operation specified");
+
+ } else if (strcmp(PCMK__CIB_REQUEST_NOOP, op) != 0) {
+ result = -EPROTONOSUPPORT;
+ crm_err("Action [%s] is not supported by the CIB manager", op);
+ }
+ return result;
+}
+
+int
+cib_process_readwrite(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ int result = pcmk_ok;
+
+ crm_trace("Processing \"%s\" event", op);
+
+ if (pcmk__str_eq(op, PCMK__CIB_REQUEST_IS_PRIMARY, pcmk__str_none)) {
+ if (based_is_primary) {
+ result = pcmk_ok;
+ } else {
+ result = -EPERM;
+ }
+ return result;
+ }
+
+ if (pcmk__str_eq(op, PCMK__CIB_REQUEST_PRIMARY, pcmk__str_none)) {
+ if (!based_is_primary) {
+ crm_info("We are now in R/W mode");
+ based_is_primary = true;
+ } else {
+ crm_debug("We are still in R/W mode");
+ }
+
+ } else if (based_is_primary) {
+ crm_info("We are now in R/O mode");
+ based_is_primary = false;
+ }
+
+ return result;
+}
+
+/* Set to 1 when a sync is requested, incremented when a diff is ignored,
+ * reset to 0 when a sync is received
+ */
+static int sync_in_progress = 0;
+
+void
+send_sync_request(const char *host)
+{
+ xmlNode *sync_me = create_xml_node(NULL, "sync-me");
+
+ crm_info("Requesting re-sync from %s", (host? host : "all peers"));
+ sync_in_progress = 1;
+
+ crm_xml_add(sync_me, F_TYPE, "cib");
+ crm_xml_add(sync_me, F_CIB_OPERATION, PCMK__CIB_REQUEST_SYNC_TO_ONE);
+ crm_xml_add(sync_me, F_CIB_DELEGATED,
+ stand_alone? "localhost" : crm_cluster->uname);
+
+ send_cluster_message(host ? crm_get_peer(0, host) : NULL, crm_msg_cib, sync_me, FALSE);
+ free_xml(sync_me);
+}
+
+int
+cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
+ xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
+{
+ const char *host = crm_element_value(req, F_ORIG);
+ const char *seq = crm_element_value(req, F_CIB_PING_ID);
+ char *digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
+
+ crm_trace("Processing \"%s\" event %s from %s", op, seq, host);
+ *answer = create_xml_node(NULL, XML_CRM_TAG_PING);
+
+ crm_xml_add(*answer, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(*answer, XML_ATTR_DIGEST, digest);
+ crm_xml_add(*answer, F_CIB_PING_ID, seq);
+
+ pcmk__if_tracing(
+ {
+ // Append additional detail so the receiver can log the differences
+ add_message_xml(*answer, F_CIB_CALLDATA, the_cib);
+ },
+ {
+ // Always include at least the version details
+ const char *tag = TYPE(the_cib);
+ xmlNode *shallow = create_xml_node(NULL, tag);
+
+ copy_in_properties(shallow, the_cib);
+ add_message_xml(*answer, F_CIB_CALLDATA, shallow);
+ free_xml(shallow);
+ }
+ );
+
+ crm_info("Reporting our current digest to %s: %s for %s.%s.%s",
+ host, digest,
+ crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN),
+ crm_element_value(existing_cib, XML_ATTR_GENERATION),
+ crm_element_value(existing_cib, XML_ATTR_NUMUPDATES));
+
+ free(digest);
+
+ return pcmk_ok;
+}
+
+int
+cib_process_sync(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
+ xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
+{
+ return sync_our_cib(req, TRUE);
+}
+
+int
+cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
+ xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
+{
+ int rc = pcmk_ok;
+
+ *answer = NULL;
+
+ if(crm_element_value(req, F_CIB_SCHEMA_MAX)) {
+ /* The originator of an upgrade request sends it to the DC, without
+ * F_CIB_SCHEMA_MAX. If an upgrade is needed, the DC re-broadcasts the
+ * request with F_CIB_SCHEMA_MAX, and each node performs the upgrade
+ * (and notifies its local clients) here.
+ */
+ return cib_process_upgrade(
+ op, options, section, req, input, existing_cib, result_cib, answer);
+
+ } else {
+ int new_version = 0;
+ int current_version = 0;
+ xmlNode *scratch = copy_xml(existing_cib);
+ const char *host = crm_element_value(req, F_ORIG);
+ const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);
+ const char *client_id = crm_element_value(req, F_CIB_CLIENTID);
+ const char *call_opts = crm_element_value(req, F_CIB_CALLOPTS);
+ const char *call_id = crm_element_value(req, F_CIB_CALLID);
+
+ crm_trace("Processing \"%s\" event", op);
+ if (value != NULL) {
+ current_version = get_schema_version(value);
+ }
+
+ rc = update_validation(&scratch, &new_version, 0, TRUE, TRUE);
+ if (new_version > current_version) {
+ xmlNode *up = create_xml_node(NULL, __func__);
+
+ rc = pcmk_ok;
+ crm_notice("Upgrade request from %s verified", host);
+
+ crm_xml_add(up, F_TYPE, "cib");
+ crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE);
+ crm_xml_add(up, F_CIB_SCHEMA_MAX, get_schema_name(new_version));
+ crm_xml_add(up, F_CIB_DELEGATED, host);
+ crm_xml_add(up, F_CIB_CLIENTID, client_id);
+ crm_xml_add(up, F_CIB_CALLOPTS, call_opts);
+ crm_xml_add(up, F_CIB_CALLID, call_id);
+
+ if (cib_legacy_mode() && based_is_primary) {
+ rc = cib_process_upgrade(
+ op, options, section, up, input, existing_cib, result_cib, answer);
+
+ } else {
+ send_cluster_message(NULL, crm_msg_cib, up, FALSE);
+ }
+
+ free_xml(up);
+
+ } else if(rc == pcmk_ok) {
+ rc = -pcmk_err_schema_unchanged;
+ }
+
+ if (rc != pcmk_ok) {
+ // Notify originating peer so it can notify its local clients
+ crm_node_t *origin = pcmk__search_cluster_node_cache(0, host);
+
+ crm_info("Rejecting upgrade request from %s: %s "
+ CRM_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc,
+ (origin? origin->uname : "lost"));
+
+ if (origin) {
+ xmlNode *up = create_xml_node(NULL, __func__);
+
+ crm_xml_add(up, F_TYPE, "cib");
+ crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE);
+ crm_xml_add(up, F_CIB_DELEGATED, host);
+ crm_xml_add(up, F_CIB_ISREPLY, host);
+ crm_xml_add(up, F_CIB_CLIENTID, client_id);
+ crm_xml_add(up, F_CIB_CALLOPTS, call_opts);
+ crm_xml_add(up, F_CIB_CALLID, call_id);
+ crm_xml_add_int(up, F_CIB_UPGRADE_RC, rc);
+ if (send_cluster_message(origin, crm_msg_cib, up, TRUE)
+ == FALSE) {
+ crm_warn("Could not send CIB upgrade result to %s", host);
+ }
+ free_xml(up);
+ }
+ }
+ free_xml(scratch);
+ }
+ return rc;
+}
+
+int
+cib_process_sync_one(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ return sync_our_cib(req, FALSE);
+}
+
+int
+cib_server_process_diff(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ int rc = pcmk_ok;
+
+ if (sync_in_progress > MAX_DIFF_RETRY) {
+ /* Don't ignore diffs forever; the last request may have been lost.
+ * If the diff fails, we'll ask for another full resync.
+ */
+ sync_in_progress = 0;
+ }
+
+ // The primary instance should never ignore a diff
+ if (sync_in_progress && !based_is_primary) {
+ int diff_add_updates = 0;
+ int diff_add_epoch = 0;
+ int diff_add_admin_epoch = 0;
+
+ int diff_del_updates = 0;
+ int diff_del_epoch = 0;
+ int diff_del_admin_epoch = 0;
+
+ cib_diff_version_details(input,
+ &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
+ &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
+
+ sync_in_progress++;
+ crm_notice("Not applying diff %d.%d.%d -> %d.%d.%d (sync in progress)",
+ diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
+ diff_add_admin_epoch, diff_add_epoch, diff_add_updates);
+ return -pcmk_err_diff_resync;
+ }
+
+ rc = cib_process_diff(op, options, section, req, input, existing_cib, result_cib, answer);
+ crm_trace("result: %s (%d), %s", pcmk_strerror(rc), rc,
+ (based_is_primary? "primary": "secondary"));
+
+ if ((rc == -pcmk_err_diff_resync) && !based_is_primary) {
+ free_xml(*result_cib);
+ *result_cib = NULL;
+ send_sync_request(NULL);
+
+ } else if (rc == -pcmk_err_diff_resync) {
+ rc = -pcmk_err_diff_failed;
+ if (options & cib_force_diff) {
+ crm_warn("Not requesting full refresh in R/W mode");
+ }
+
+ } else if ((rc != pcmk_ok) && !based_is_primary && cib_legacy_mode()) {
+ crm_warn("Requesting full CIB refresh because update failed: %s"
+ CRM_XS " rc=%d", pcmk_strerror(rc), rc);
+
+ pcmk__output_set_log_level(logger_out, LOG_INFO);
+ logger_out->message(logger_out, "xml-patchset", input);
+ free_xml(*result_cib);
+ *result_cib = NULL;
+ send_sync_request(NULL);
+ }
+
+ return rc;
+}
+
+int
+cib_process_replace_svr(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ const char *tag = crm_element_name(input);
+ int rc =
+ cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer);
+ if (rc == pcmk_ok && pcmk__str_eq(tag, XML_TAG_CIB, pcmk__str_casei)) {
+ sync_in_progress = 0;
+ }
+ return rc;
+}
+
+int
+cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode * req,
+ xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
+ xmlNode ** answer)
+{
+ return -EINVAL;
+}
+
+int
+sync_our_cib(xmlNode * request, gboolean all)
+{
+ int result = pcmk_ok;
+ char *digest = NULL;
+ const char *host = crm_element_value(request, F_ORIG);
+ const char *op = crm_element_value(request, F_CIB_OPERATION);
+
+ xmlNode *replace_request = NULL;
+
+ CRM_CHECK(the_cib != NULL, return -EINVAL);
+
+ replace_request = cib_msg_copy(request, FALSE);
+ CRM_CHECK(replace_request != NULL, return -EINVAL);
+
+ crm_debug("Syncing CIB to %s", all ? "all peers" : host);
+ if (all == FALSE && host == NULL) {
+ crm_log_xml_err(request, "bad sync");
+ }
+
+ /* remove the "all == FALSE" condition
+ *
+ * sync_from was failing, the local client wasn't being notified
+ * because it didn't know it was a reply
+ * setting this does not prevent the other nodes from applying it
+ * if all == TRUE
+ */
+ if (host != NULL) {
+ crm_xml_add(replace_request, F_CIB_ISREPLY, host);
+ }
+ if (all) {
+ xml_remove_prop(replace_request, F_CIB_HOST);
+ }
+
+ crm_xml_add(replace_request, F_CIB_OPERATION, PCMK__CIB_REQUEST_REPLACE);
+ crm_xml_add(replace_request, "original_" F_CIB_OPERATION, op);
+ pcmk__xe_set_bool_attr(replace_request, F_CIB_GLOBAL_UPDATE, true);
+
+ crm_xml_add(replace_request, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
+ digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
+ crm_xml_add(replace_request, XML_ATTR_DIGEST, digest);
+
+ add_message_xml(replace_request, F_CIB_CALLDATA, the_cib);
+
+ if (send_cluster_message
+ (all ? NULL : crm_get_peer(0, host), crm_msg_cib, replace_request, FALSE) == FALSE) {
+ result = -ENOTCONN;
+ }
+ free_xml(replace_request);
+ free(digest);
+ return result;
+}