Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4624731
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
113 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c
index eb77114684..61eb4944be 100644
--- a/daemons/based/based_callbacks.c
+++ b/daemons/based/based_callbacks.c
@@ -1,1709 +1,1776 @@
/*
* 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 <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h> // uint32_t, uint64_t, UINT64_C()
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h> // PRIu64
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/cluster/internal.h>
#include <crm/common/xml.h>
#include <crm/common/remote_internal.h>
#include <pacemaker-based.h>
#define EXIT_ESCALATION_MS 10000
#define OUR_NODENAME (stand_alone? "localhost" : crm_cluster->uname)
static unsigned long cib_local_bcast_num = 0;
typedef struct cib_local_notify_s {
xmlNode *notify_src;
char *client_id;
gboolean from_peer;
gboolean sync_reply;
} cib_local_notify_t;
+struct digest_data {
+ char *nodes;
+ char *alerts;
+ char *status;
+};
+
int next_client_id = 0;
gboolean legacy_mode = FALSE;
qb_ipcs_service_t *ipcs_ro = NULL;
qb_ipcs_service_t *ipcs_rw = NULL;
qb_ipcs_service_t *ipcs_shm = NULL;
static void cib_process_request(xmlNode *request, gboolean privileged,
const pcmk__client_t *cib_client);
-static int cib_process_command(xmlNode *request, xmlNode **reply,
- xmlNode **cib_diff, gboolean privileged);
+static int cib_process_command(xmlNode *request,
+ const cib_operation_t *operation,
+ xmlNode **reply, xmlNode **cib_diff,
+ bool privileged);
static gboolean cib_common_callback(qb_ipcs_connection_t *c, void *data,
size_t size, gboolean privileged);
gboolean
cib_legacy_mode(void)
{
return legacy_mode;
}
+/*!
+ * \internal
+ * \brief Free a <tt>struct digest_data</tt> object's members
+ *
+ * \param[in,out] digests Object whose members to free
+ *
+ * \note This does not free the object itself, which may be stack-allocated.
+ */
+static void
+free_digests(struct digest_data *digests)
+{
+ if (digests != NULL) {
+ free(digests->nodes);
+ free(digests->alerts);
+ free(digests->status);
+ }
+}
+
static int32_t
cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
if (cib_shutdown_flag) {
crm_info("Ignoring new IPC client [%d] during shutdown",
pcmk__client_pid(c));
return -EPERM;
}
if (pcmk__new_client(c, uid, gid) == NULL) {
return -EIO;
}
return 0;
}
static int32_t
cib_ipc_dispatch_rw(qb_ipcs_connection_t * c, void *data, size_t size)
{
pcmk__client_t *client = pcmk__find_client(c);
crm_trace("%p message from %s", c, client->id);
return cib_common_callback(c, data, size, TRUE);
}
static int32_t
cib_ipc_dispatch_ro(qb_ipcs_connection_t * c, void *data, size_t size)
{
pcmk__client_t *client = pcmk__find_client(c);
crm_trace("%p message from %s", c, client->id);
return cib_common_callback(c, data, size, FALSE);
}
/* Error code means? */
static int32_t
cib_ipc_closed(qb_ipcs_connection_t * c)
{
pcmk__client_t *client = pcmk__find_client(c);
if (client == NULL) {
return 0;
}
crm_trace("Connection %p", c);
pcmk__free_client(client);
return 0;
}
static void
cib_ipc_destroy(qb_ipcs_connection_t * c)
{
crm_trace("Connection %p", c);
cib_ipc_closed(c);
if (cib_shutdown_flag) {
cib_shutdown(0);
}
}
struct qb_ipcs_service_handlers ipc_ro_callbacks = {
.connection_accept = cib_ipc_accept,
.connection_created = NULL,
.msg_process = cib_ipc_dispatch_ro,
.connection_closed = cib_ipc_closed,
.connection_destroyed = cib_ipc_destroy
};
struct qb_ipcs_service_handlers ipc_rw_callbacks = {
.connection_accept = cib_ipc_accept,
.connection_created = NULL,
.msg_process = cib_ipc_dispatch_rw,
.connection_closed = cib_ipc_closed,
.connection_destroyed = cib_ipc_destroy
};
+/*!
+ * \internal
+ * \brief Create reply XML for a CIB request
+ *
+ * \param[in] op CIB operation type
+ * \param[in] call_id CIB call ID
+ * \param[in] client_id CIB client ID
+ * \param[in] call_options Group of <tt>enum cib_call_options</tt> flags
+ * \param[in] rc Request return code
+ * \param[in] call_data Request output data
+ *
+ * \return Reply XML
+ *
+ * \note The caller is responsible for freeing the return value using
+ * \p free_xml().
+ */
+static xmlNode *
+create_cib_reply(const char *op, const char *call_id, const char *client_id,
+ int call_options, int rc, xmlNode *call_data)
+{
+ xmlNode *reply = create_xml_node(NULL, "cib-reply");
+
+ CRM_ASSERT(reply != NULL);
+
+ crm_xml_add(reply, F_TYPE, T_CIB);
+ crm_xml_add(reply, F_CIB_OPERATION, op);
+ crm_xml_add(reply, F_CIB_CALLID, call_id);
+ crm_xml_add(reply, F_CIB_CLIENTID, client_id);
+ crm_xml_add_int(reply, F_CIB_CALLOPTS, call_options);
+ crm_xml_add_int(reply, F_CIB_RC, rc);
+
+ if (call_data != NULL) {
+ crm_trace("Attaching reply output");
+ add_message_xml(reply, F_CIB_CALLDATA, call_data);
+ }
+
+ crm_log_xml_explicit(reply, "cib:reply");
+ return reply;
+}
+
void
cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
pcmk__client_t *cib_client, gboolean privileged)
{
const char *op = crm_element_value(op_request, F_CIB_OPERATION);
if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) {
if (flags & crm_ipc_client_response) {
xmlNode *ack = create_xml_node(NULL, __func__);
crm_xml_add(ack, F_CIB_OPERATION, CRM_OP_REGISTER);
crm_xml_add(ack, F_CIB_CLIENTID, cib_client->id);
pcmk__ipc_send_xml(cib_client, id, ack, flags);
cib_client->request_id = 0;
free_xml(ack);
}
return;
} else if (pcmk__str_eq(op, T_CIB_NOTIFY, pcmk__str_none)) {
/* Update the notify filters for this client */
int on_off = 0;
crm_exit_t status = CRM_EX_OK;
uint64_t bit = UINT64_C(0);
const char *type = crm_element_value(op_request, F_CIB_NOTIFY_TYPE);
crm_element_value_int(op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
crm_debug("Setting %s callbacks %s for client %s",
type, (on_off? "on" : "off"), pcmk__client_name(cib_client));
if (pcmk__str_eq(type, T_CIB_POST_NOTIFY, pcmk__str_casei)) {
bit = cib_notify_post;
} else if (pcmk__str_eq(type, T_CIB_PRE_NOTIFY, pcmk__str_casei)) {
bit = cib_notify_pre;
} else if (pcmk__str_eq(type, T_CIB_UPDATE_CONFIRM, pcmk__str_casei)) {
bit = cib_notify_confirm;
} else if (pcmk__str_eq(type, T_CIB_DIFF_NOTIFY, pcmk__str_casei)) {
bit = cib_notify_diff;
} else if (pcmk__str_eq(type, T_CIB_REPLACE_NOTIFY, pcmk__str_casei)) {
bit = cib_notify_replace;
} else {
status = CRM_EX_INVALID_PARAM;
}
if (bit != 0) {
if (on_off) {
pcmk__set_client_flags(cib_client, bit);
} else {
pcmk__clear_client_flags(cib_client, bit);
}
}
pcmk__ipc_send_ack(cib_client, id, flags, "ack", NULL, status);
return;
}
cib_process_request(op_request, privileged, cib_client);
}
int32_t
cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean privileged)
{
uint32_t id = 0;
uint32_t flags = 0;
int call_options = 0;
pcmk__client_t *cib_client = pcmk__find_client(c);
xmlNode *op_request = pcmk__client_data2xml(cib_client, data, &id, &flags);
if (op_request) {
crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
}
if (op_request == NULL) {
crm_trace("Invalid message from %p", c);
pcmk__ipc_send_ack(cib_client, id, flags, "nack", NULL, CRM_EX_PROTOCOL);
return 0;
} else if(cib_client == NULL) {
crm_trace("Invalid client %p", c);
return 0;
}
if (pcmk_is_set(call_options, cib_sync_call)) {
CRM_LOG_ASSERT(flags & crm_ipc_client_response);
CRM_LOG_ASSERT(cib_client->request_id == 0); /* This means the client has two synchronous events in-flight */
cib_client->request_id = id; /* Reply only to the last one */
}
if (cib_client->name == NULL) {
const char *value = crm_element_value(op_request, F_CIB_CLIENTNAME);
if (value == NULL) {
cib_client->name = pcmk__itoa(cib_client->pid);
} else {
cib_client->name = strdup(value);
if (crm_is_daemon_name(value)) {
pcmk__set_client_flags(cib_client, cib_is_daemon);
}
}
}
/* Allow cluster daemons more leeway before being evicted */
if (pcmk_is_set(cib_client->flags, cib_is_daemon)) {
const char *qmax = cib_config_lookup("cluster-ipc-limit");
if (pcmk__set_client_queue_max(cib_client, qmax)) {
crm_trace("IPC threshold for client %s[%u] is now %u",
pcmk__client_name(cib_client), cib_client->pid,
cib_client->queue_max);
}
}
crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id);
crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
CRM_LOG_ASSERT(cib_client->user != NULL);
pcmk__update_acl_user(op_request, F_CIB_USER, cib_client->user);
cib_common_callback_worker(id, flags, op_request, cib_client, privileged);
free_xml(op_request);
return 0;
}
static uint64_t ping_seq = 0;
static char *ping_digest = NULL;
static bool ping_modified_since = FALSE;
static gboolean
cib_digester_cb(gpointer data)
{
if (based_is_primary) {
char buffer[32];
xmlNode *ping = create_xml_node(NULL, "ping");
ping_seq++;
free(ping_digest);
ping_digest = NULL;
ping_modified_since = FALSE;
snprintf(buffer, 32, "%" PRIu64, ping_seq);
crm_trace("Requesting peer digests (%s)", buffer);
crm_xml_add(ping, F_TYPE, "cib");
crm_xml_add(ping, F_CIB_OPERATION, CRM_OP_PING);
crm_xml_add(ping, F_CIB_PING_ID, buffer);
crm_xml_add(ping, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
send_cluster_message(NULL, crm_msg_cib, ping, TRUE);
free_xml(ping);
}
return FALSE;
}
static void
process_ping_reply(xmlNode *reply)
{
uint64_t seq = 0;
const char *host = crm_element_value(reply, F_ORIG);
xmlNode *pong = get_message_xml(reply, F_CIB_CALLDATA);
const char *seq_s = crm_element_value(pong, F_CIB_PING_ID);
const char *digest = crm_element_value(pong, XML_ATTR_DIGEST);
if (seq_s == NULL) {
crm_debug("Ignoring ping reply with no " F_CIB_PING_ID);
return;
} else {
long long seq_ll;
if (pcmk__scan_ll(seq_s, &seq_ll, 0LL) != pcmk_rc_ok) {
return;
}
seq = (uint64_t) seq_ll;
}
if(digest == NULL) {
crm_trace("Ignoring ping reply %s from %s with no digest", seq_s, host);
} else if(seq != ping_seq) {
crm_trace("Ignoring out of sequence ping reply %s from %s", seq_s, host);
} else if(ping_modified_since) {
crm_trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host);
} else {
const char *version = crm_element_value(pong, XML_ATTR_CRM_VERSION);
if(ping_digest == NULL) {
crm_trace("Calculating new digest");
ping_digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, version);
}
crm_trace("Processing ping reply %s from %s (%s)", seq_s, host, digest);
if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) {
xmlNode *remote_cib = get_message_xml(pong, F_CIB_CALLDATA);
crm_notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s %p",
crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN),
crm_element_value(the_cib, XML_ATTR_GENERATION),
crm_element_value(the_cib, XML_ATTR_NUMUPDATES),
ping_digest, host,
remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION_ADMIN):"_",
remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION):"_",
remote_cib?crm_element_value(remote_cib, XML_ATTR_NUMUPDATES):"_",
digest, remote_cib);
if(remote_cib && remote_cib->children) {
// Additional debug
xml_calculate_changes(the_cib, remote_cib);
pcmk__output_set_log_level(logger_out, LOG_INFO);
pcmk__xml_show_changes(logger_out, remote_cib);
crm_trace("End of differences");
}
free_xml(remote_cib);
sync_our_cib(reply, FALSE);
}
}
}
static void
do_local_notify(xmlNode * notify_src, const char *client_id,
gboolean sync_reply, gboolean from_peer)
{
int rid = 0;
int call_id = 0;
pcmk__client_t *client_obj = NULL;
CRM_ASSERT(notify_src && client_id);
crm_element_value_int(notify_src, F_CIB_CALLID, &call_id);
client_obj = pcmk__find_client_by_id(client_id);
if (client_obj == NULL) {
crm_debug("Could not send response %d: client %s not found",
call_id, client_id);
return;
}
if (sync_reply) {
if (client_obj->ipcs) {
CRM_LOG_ASSERT(client_obj->request_id);
rid = client_obj->request_id;
client_obj->request_id = 0;
crm_trace("Sending response %d to client %s%s",
rid, pcmk__client_name(client_obj),
(from_peer? " (originator of delegated request)" : ""));
} else {
crm_trace("Sending response (call %d) to client %s%s",
call_id, pcmk__client_name(client_obj),
(from_peer? " (originator of delegated request)" : ""));
}
} else {
crm_trace("Sending event %d to client %s%s",
call_id, pcmk__client_name(client_obj),
(from_peer? " (originator of delegated request)" : ""));
}
switch (PCMK__CLIENT_TYPE(client_obj)) {
case pcmk__client_ipc:
{
int rc = pcmk__ipc_send_xml(client_obj, rid, notify_src,
(sync_reply? crm_ipc_flags_none
: crm_ipc_server_event));
if (rc != pcmk_rc_ok) {
crm_warn("%s reply to client %s failed: %s " CRM_XS " rc=%d",
(sync_reply? "Synchronous" : "Asynchronous"),
pcmk__client_name(client_obj), pcmk_rc_str(rc),
rc);
}
}
break;
#ifdef HAVE_GNUTLS_GNUTLS_H
case pcmk__client_tls:
#endif
case pcmk__client_tcp:
pcmk__remote_send_xml(client_obj->remote, notify_src);
break;
default:
crm_err("Unknown transport for client %s "
CRM_XS " flags=%#016" PRIx64,
pcmk__client_name(client_obj), client_obj->flags);
}
}
static void
local_notify_destroy_callback(gpointer data)
{
cib_local_notify_t *notify = data;
free_xml(notify->notify_src);
free(notify->client_id);
free(notify);
}
static void
check_local_notify(int bcast_id)
{
cib_local_notify_t *notify = NULL;
if (!local_notify_queue) {
return;
}
notify = pcmk__intkey_table_lookup(local_notify_queue, bcast_id);
if (notify) {
do_local_notify(notify->notify_src, notify->client_id, notify->sync_reply,
notify->from_peer);
pcmk__intkey_table_remove(local_notify_queue, bcast_id);
}
}
static void
queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_reply,
gboolean from_peer)
{
cib_local_notify_t *notify = calloc(1, sizeof(cib_local_notify_t));
notify->notify_src = notify_src;
notify->client_id = strdup(client_id);
notify->sync_reply = sync_reply;
notify->from_peer = from_peer;
if (!local_notify_queue) {
local_notify_queue = pcmk__intkey_table(local_notify_destroy_callback);
}
pcmk__intkey_table_insert(local_notify_queue, cib_local_bcast_num, notify);
// cppcheck doesn't know notify will get freed when hash table is destroyed
// cppcheck-suppress memleak
}
static void
parse_local_options_v1(const pcmk__client_t *cib_client,
const cib_operation_t *operation, int call_options,
const char *host, const char *op, gboolean *local_notify,
gboolean *needs_reply, gboolean *process,
gboolean *needs_forward)
{
if (pcmk_is_set(operation->flags, cib_op_attr_modifies)
&& !pcmk_is_set(call_options, cib_inhibit_bcast)) {
/* we need to send an update anyway */
*needs_reply = TRUE;
} else {
*needs_reply = FALSE;
}
if (host == NULL && (call_options & cib_scope_local)) {
crm_trace("Processing locally scoped %s op from client %s",
op, pcmk__client_name(cib_client));
*local_notify = TRUE;
} else if ((host == NULL) && based_is_primary) {
crm_trace("Processing %s op locally from client %s as primary",
op, pcmk__client_name(cib_client));
*local_notify = TRUE;
} else if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
crm_trace("Processing locally addressed %s op from client %s",
op, pcmk__client_name(cib_client));
*local_notify = TRUE;
} else if (stand_alone) {
*needs_forward = FALSE;
*local_notify = TRUE;
*process = TRUE;
} else {
crm_trace("%s op from %s needs to be forwarded to client %s",
op, pcmk__client_name(cib_client),
pcmk__s(host, "the primary instance"));
*needs_forward = TRUE;
*process = FALSE;
}
}
static void
parse_local_options_v2(const pcmk__client_t *cib_client,
const cib_operation_t *operation, int call_options,
const char *host, const char *op, gboolean *local_notify,
gboolean *needs_reply, gboolean *process,
gboolean *needs_forward)
{
- if (pcmk_is_set(operation->flags, cib_op_attr_modifies)) {
- if (pcmk__str_any_of(op, PCMK__CIB_REQUEST_PRIMARY,
- PCMK__CIB_REQUEST_SECONDARY, NULL)) {
- /* Always handle these locally */
- *process = TRUE;
- *needs_reply = FALSE;
- *local_notify = TRUE;
- *needs_forward = FALSE;
- return;
-
- } else {
- /* Redirect all other updates via CPG */
- *needs_reply = TRUE;
- *needs_forward = TRUE;
- *process = FALSE;
- crm_trace("%s op from %s needs to be forwarded to %s",
- op, pcmk__client_name(cib_client),
- pcmk__s(host, "all nodes"));
- return;
- }
- }
-
+ // Process locally and notify local client
*process = TRUE;
*needs_reply = FALSE;
*local_notify = TRUE;
*needs_forward = FALSE;
- if (stand_alone) {
- crm_trace("Processing %s op from client %s (stand-alone)",
+ if (pcmk_is_set(operation->flags, cib_op_attr_local)) {
+ /* Always process locally if cib_op_attr_local is set.
+ *
+ * @COMPAT: Currently host is ignored. At a compatibility break, throw
+ * an error (from cib_process_request() or earlier) if host is not NULL or
+ * OUR_NODENAME.
+ */
+ crm_trace("Processing always-local %s op from client %s",
op, pcmk__client_name(cib_client));
- } else if (host == NULL) {
- crm_trace("Processing unaddressed %s op from client %s",
- op, pcmk__client_name(cib_client));
+ if (!pcmk__str_eq(host, OUR_NODENAME,
+ pcmk__str_casei|pcmk__str_null_matches)) {
- } else if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
- crm_trace("Processing locally addressed %s op from client %s",
+ crm_warn("Operation '%s' is always local but its target host is "
+ "set to '%s'",
+ op, host);
+ }
+ return;
+ }
+
+ if (pcmk_is_set(operation->flags, cib_op_attr_modifies)
+ || !pcmk__str_eq(host, OUR_NODENAME,
+ pcmk__str_casei|pcmk__str_null_matches)) {
+
+ // Forward modifying and non-local requests via cluster
+ *process = FALSE;
+ *needs_reply = FALSE;
+ *local_notify = FALSE;
+ *needs_forward = TRUE;
+
+ crm_trace("%s op from %s needs to be forwarded to %s",
+ op, pcmk__client_name(cib_client),
+ pcmk__s(host, "all nodes"));
+ return;
+ }
+
+ if (stand_alone) {
+ crm_trace("Processing %s op from client %s (stand-alone)",
op, pcmk__client_name(cib_client));
} else {
- crm_trace("%s op from %s needs to be forwarded to client %s",
- op, pcmk__client_name(cib_client), host);
- *needs_forward = TRUE;
- *process = FALSE;
+ crm_trace("Processing %saddressed %s op from client %s",
+ ((host != NULL)? "locally " : "un"),
+ op, pcmk__client_name(cib_client));
}
}
static void
parse_local_options(const pcmk__client_t *cib_client,
const cib_operation_t *operation, int call_options,
const char *host, const char *op, gboolean *local_notify,
gboolean *needs_reply, gboolean *process,
gboolean *needs_forward)
{
if(cib_legacy_mode()) {
parse_local_options_v1(cib_client, operation, call_options, host,
op, local_notify, needs_reply, process,
needs_forward);
} else {
parse_local_options_v2(cib_client, operation, call_options, host,
op, local_notify, needs_reply, process,
needs_forward);
}
}
static gboolean
parse_peer_options_v1(const cib_operation_t *operation, xmlNode *request,
gboolean *local_notify, gboolean *needs_reply,
- gboolean *process, gboolean *needs_forward)
+ gboolean *process)
{
const char *op = NULL;
const char *host = NULL;
const char *delegated = NULL;
const char *originator = crm_element_value(request, F_ORIG);
const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei);
if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
*needs_reply = FALSE;
if (is_reply) {
*local_notify = TRUE;
crm_trace("Processing global/peer update from %s"
" that originated from us", originator);
} else {
crm_trace("Processing global/peer update from %s", originator);
}
return TRUE;
}
op = crm_element_value(request, F_CIB_OPERATION);
crm_trace("Processing %s request sent by %s", op, originator);
if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
/* Always process these */
*local_notify = FALSE;
if (reply_to == NULL || is_reply) {
*process = TRUE;
}
if (is_reply) {
*needs_reply = FALSE;
}
return *process;
}
if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
process_ping_reply(request);
return FALSE;
}
if (is_reply) {
crm_trace("Forward reply sent from %s to local clients", originator);
*process = FALSE;
*needs_reply = FALSE;
*local_notify = TRUE;
return TRUE;
}
host = crm_element_value(request, F_CIB_HOST);
if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
crm_trace("Processing %s request sent to us from %s", op, originator);
return TRUE;
} else if(is_reply == FALSE && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
crm_trace("Processing %s request sent to %s by %s", op, host?host:"everyone", originator);
*needs_reply = TRUE;
return TRUE;
} else if ((host == NULL) && based_is_primary) {
crm_trace("Processing %s request sent to primary instance from %s",
op, originator);
return TRUE;
}
delegated = crm_element_value(request, F_CIB_DELEGATED);
if (delegated != NULL) {
crm_trace("Ignoring message for primary instance");
} else if (host != NULL) {
/* this is for a specific instance and we're not it */
crm_trace("Ignoring msg for instance on %s", host);
} else if ((reply_to == NULL) && !based_is_primary) {
// This is for the primary instance, and we're not it
crm_trace("Ignoring reply for primary instance");
} else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
if (reply_to != NULL) {
crm_debug("Processing %s from %s", op, originator);
*needs_reply = FALSE;
} else {
crm_debug("Processing %s reply from %s", op, originator);
}
return TRUE;
} else {
crm_err("Nothing for us to do?");
crm_log_xml_err(request, "Peer[inbound]");
}
return FALSE;
}
static gboolean
parse_peer_options_v2(const cib_operation_t *operation, xmlNode *request,
gboolean *local_notify, gboolean *needs_reply,
- gboolean *process, gboolean *needs_forward)
+ gboolean *process)
{
const char *host = NULL;
const char *delegated = crm_element_value(request, F_CIB_DELEGATED);
const char *op = crm_element_value(request, F_CIB_OPERATION);
const char *originator = crm_element_value(request, F_ORIG);
const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei);
if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) {
/* sync_our_cib() sets F_CIB_ISREPLY */
if (reply_to) {
delegated = reply_to;
}
goto skip_is_reply;
} else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SYNC_TO_ALL,
pcmk__str_none)) {
// Nothing to do
} else if (is_reply && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
process_ping_reply(request);
return FALSE;
} else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) {
/* Only the DC (node with the oldest software) should process
* this operation if F_CIB_SCHEMA_MAX is unset
*
* If the DC is happy it will then send out another
* PCMK__CIB_REQUEST_UPGRADE which will tell all nodes to do the actual
* upgrade.
*
* Except this time F_CIB_SCHEMA_MAX will be set which puts a
* limit on how far newer nodes will go
*/
const char *max = crm_element_value(request, F_CIB_SCHEMA_MAX);
const char *upgrade_rc = crm_element_value(request, F_CIB_UPGRADE_RC);
crm_trace("Parsing %s operation%s for %s with max=%s and upgrade_rc=%s",
op, (is_reply? " reply" : ""),
(based_is_primary? "primary" : "secondary"),
(max? max : "none"), (upgrade_rc? upgrade_rc : "none"));
if (upgrade_rc != NULL) {
// Our upgrade request was rejected by DC, notify clients of result
crm_xml_add(request, F_CIB_RC, upgrade_rc);
} else if ((max == NULL) && based_is_primary) {
/* We are the DC, check if this upgrade is allowed */
goto skip_is_reply;
} else if(max) {
/* Ok, go ahead and upgrade to 'max' */
goto skip_is_reply;
} else {
// Ignore broadcast client requests when we're not DC
return FALSE;
}
} else if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
crm_info("Detected legacy %s global update from %s", op, originator);
send_sync_request(NULL);
legacy_mode = TRUE;
return FALSE;
} else if (is_reply
&& pcmk_is_set(operation->flags, cib_op_attr_modifies)) {
crm_trace("Ignoring legacy %s reply sent from %s to local clients", op, originator);
return FALSE;
} else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
- /* Legacy handling */
+ // @COMPAT: Legacy handling
crm_debug("Legacy handling of %s message from %s", op, originator);
*local_notify = FALSE;
if (reply_to == NULL) {
*process = TRUE;
}
return *process;
}
if(is_reply) {
crm_trace("Handling %s reply sent from %s to local clients", op, originator);
*process = FALSE;
*needs_reply = FALSE;
*local_notify = TRUE;
return TRUE;
}
skip_is_reply:
*process = TRUE;
*needs_reply = FALSE;
*local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei);
host = crm_element_value(request, F_CIB_HOST);
if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
crm_trace("Processing %s request sent to us from %s", op, originator);
*needs_reply = TRUE;
return TRUE;
} else if (host != NULL) {
/* this is for a specific instance and we're not it */
crm_trace("Ignoring %s operation for instance on %s", op, host);
return FALSE;
} else if(is_reply == FALSE && pcmk__str_eq(op, CRM_OP_PING, pcmk__str_casei)) {
*needs_reply = TRUE;
}
crm_trace("Processing %s request sent to everyone by %s/%s on %s %s", op,
crm_element_value(request, F_CIB_CLIENTNAME),
crm_element_value(request, F_CIB_CALLID),
originator, (*local_notify)?"(notify)":"");
return TRUE;
}
static gboolean
parse_peer_options(const cib_operation_t *operation, xmlNode *request,
gboolean *local_notify, gboolean *needs_reply,
- gboolean *process, gboolean *needs_forward)
+ gboolean *process)
{
/* TODO: What happens when an update comes in after node A
* requests the CIB from node B, but before it gets the reply (and
* sends out the replace operation)
*/
if(cib_legacy_mode()) {
return parse_peer_options_v1(operation, request, local_notify,
- needs_reply, process, needs_forward);
+ needs_reply, process);
} else {
return parse_peer_options_v2(operation, request, local_notify,
- needs_reply, process, needs_forward);
+ needs_reply, process);
}
}
+/*!
+ * \internal
+ * \brief Forward a CIB request to the appropriate target host(s)
+ *
+ * \param[in] request CIB request to forward
+ */
static void
-forward_request(xmlNode *request, int call_options)
+forward_request(xmlNode *request)
{
const char *op = crm_element_value(request, F_CIB_OPERATION);
+ const char *section = crm_element_value(request, F_CIB_SECTION);
const char *host = crm_element_value(request, F_CIB_HOST);
+ const char *originator = crm_element_value(request, F_ORIG);
+ const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
+ const char *call_id = crm_element_value(request, F_CIB_CALLID);
- crm_xml_add(request, F_CIB_DELEGATED, OUR_NODENAME);
-
- if (host != NULL) {
- crm_trace("Forwarding %s op to %s", op, host);
- send_cluster_message(crm_get_peer(0, host), crm_msg_cib, request, FALSE);
+ int log_level = LOG_INFO;
- } else {
- crm_trace("Forwarding %s op to primary instance", op);
- send_cluster_message(NULL, crm_msg_cib, request, FALSE);
+ if (pcmk__str_eq(op, PCMK__CIB_REQUEST_NOOP, pcmk__str_none)) {
+ log_level = LOG_DEBUG;
}
- /* Return the request to its original state */
- xml_remove_prop(request, F_CIB_DELEGATED);
+ do_crm_log(log_level,
+ "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)",
+ pcmk__s(op, "invalid"),
+ pcmk__s(section, "all"),
+ pcmk__s(host, (cib_legacy_mode()? "primary" : "all")),
+ pcmk__s(originator, "local"),
+ pcmk__s(client_name, "unspecified"),
+ pcmk__s(call_id, "unspecified"));
- if (call_options & cib_discard_reply) {
- crm_trace("Client not interested in reply");
- }
+ crm_xml_add(request, F_CIB_DELEGATED, OUR_NODENAME);
+
+ send_cluster_message(((host != NULL)? crm_get_peer(0, host) : NULL),
+ crm_msg_cib, request, FALSE);
+
+ // Return the request to its original state
+ xml_remove_prop(request, F_CIB_DELEGATED);
}
static gboolean
send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gboolean broadcast)
{
CRM_ASSERT(msg != NULL);
if (broadcast) {
- /* this (successful) call modified the CIB _and_ the
- * change needs to be broadcast...
- * send via HA to other nodes
+ /* @COMPAT: Legacy code
+ *
+ * This successful call modified the CIB, and the change needs to be
+ * broadcast (sent via cluster to all nodes).
*/
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;
const char *digest = NULL;
int format = 1;
CRM_LOG_ASSERT(result_diff != NULL);
digest = crm_element_value(result_diff, XML_ATTR_DIGEST);
crm_element_value_int(result_diff, "format", &format);
cib_diff_version_details(result_diff,
&diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
&diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
crm_trace("Sending update diff %d.%d.%d -> %d.%d.%d %s",
diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
diff_add_admin_epoch, diff_add_epoch, diff_add_updates, digest);
crm_xml_add(msg, F_CIB_ISREPLY, originator);
pcmk__xe_set_bool_attr(msg, F_CIB_GLOBAL_UPDATE, true);
crm_xml_add(msg, F_CIB_OPERATION, PCMK__CIB_REQUEST_APPLY_PATCH);
crm_xml_add(msg, F_CIB_USER, CRM_DAEMON_USER);
if (format == 1) {
CRM_ASSERT(digest != NULL);
}
add_message_xml(msg, F_CIB_UPDATE_DIFF, result_diff);
crm_log_xml_explicit(msg, "copy");
return send_cluster_message(NULL, crm_msg_cib, msg, TRUE);
} else if (originator != NULL) {
/* send reply via HA to originating node */
crm_trace("Sending request result to %s only", originator);
crm_xml_add(msg, F_CIB_ISREPLY, originator);
return send_cluster_message(crm_get_peer(0, originator), crm_msg_cib, msg, FALSE);
}
return FALSE;
}
/*!
* \internal
* \brief Handle an IPC or CPG message containing a request
*
* \param[in,out] request Request XML
* \param[in] privileged Whether privileged commands may be run
* (see cib_server_ops[] definition)
* \param[in] cib_client IPC client that sent request (or NULL if CPG)
*/
static void
cib_process_request(xmlNode *request, gboolean privileged,
const pcmk__client_t *cib_client)
{
int call_options = 0;
gboolean process = TRUE; // Whether to process request locally now
gboolean is_update = TRUE; // Whether request would modify CIB
gboolean needs_reply = TRUE; // Whether to build a reply
gboolean local_notify = FALSE; // Whether to notify (local) requester
gboolean needs_forward = FALSE; // Whether to forward request somewhere else
xmlNode *op_reply = NULL;
xmlNode *result_diff = NULL;
int rc = pcmk_ok;
const char *op = crm_element_value(request, F_CIB_OPERATION);
const char *originator = crm_element_value(request, F_ORIG);
const char *host = crm_element_value(request, F_CIB_HOST);
const char *target = NULL;
const char *call_id = crm_element_value(request, F_CIB_CALLID);
const char *client_id = crm_element_value(request, F_CIB_CLIENTID);
const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
const cib_operation_t *operation = NULL;
crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
if ((host != NULL) && (*host == '\0')) {
host = NULL;
}
if (host) {
target = host;
} else if (call_options & cib_scope_local) {
target = "local host";
} else {
target = "primary";
}
if (cib_client == NULL) {
crm_trace("Processing peer %s operation from %s/%s on %s intended for %s (reply=%s)",
op, client_name, call_id, originator, target, reply_to);
} else {
crm_xml_add(request, F_ORIG, OUR_NODENAME);
crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target);
}
rc = cib_get_operation(op, &operation);
if (rc != pcmk_ok) {
/* TODO: construct error reply? */
crm_err("Pre-processing of command failed: %s", pcmk_strerror(rc));
return;
}
if (cib_client != NULL) {
parse_local_options(cib_client, operation, call_options, host, op,
&local_notify, &needs_reply, &process,
&needs_forward);
} else if (!parse_peer_options(operation, request, &local_notify,
- &needs_reply, &process, &needs_forward)) {
+ &needs_reply, &process)) {
return;
}
is_update = pcmk_is_set(operation->flags, cib_op_attr_modifies);
- if (call_options & cib_discard_reply) {
+ if (pcmk_is_set(call_options, cib_discard_reply)) {
/* If the request will modify the CIB, and we are in legacy mode, we
* need to build a reply so we can broadcast a diff, even if the
* requester doesn't want one.
*/
needs_reply = is_update && cib_legacy_mode();
local_notify = FALSE;
+ crm_trace("Client is not interested in the reply");
}
if (needs_forward) {
- const char *section = crm_element_value(request, F_CIB_SECTION);
- int log_level = LOG_INFO;
-
- if (pcmk__str_eq(op, PCMK__CIB_REQUEST_NOOP, pcmk__str_none)) {
- log_level = LOG_DEBUG;
- }
-
- do_crm_log(log_level,
- "Forwarding %s operation for section %s to %s (origin=%s/%s/%s)",
- op,
- section ? section : "'all'",
- pcmk__s(host, (cib_legacy_mode() ? "primary" : "all")),
- originator ? originator : "local",
- client_name, call_id);
-
- forward_request(request, call_options);
+ forward_request(request);
return;
}
if (cib_status != pcmk_ok) {
- const char *call = crm_element_value(request, F_CIB_CALLID);
-
rc = cib_status;
crm_err("Operation ignored, cluster configuration is invalid."
" Please repair and restart: %s", pcmk_strerror(cib_status));
- op_reply = create_xml_node(NULL, "cib-reply");
- crm_xml_add(op_reply, F_TYPE, T_CIB);
- crm_xml_add(op_reply, F_CIB_OPERATION, op);
- crm_xml_add(op_reply, F_CIB_CALLID, call);
- crm_xml_add(op_reply, F_CIB_CLIENTID, client_id);
- crm_xml_add_int(op_reply, F_CIB_CALLOPTS, call_options);
- crm_xml_add_int(op_reply, F_CIB_RC, rc);
-
- crm_trace("Attaching reply output");
- add_message_xml(op_reply, F_CIB_CALLDATA, the_cib);
-
- crm_log_xml_explicit(op_reply, "cib:reply");
+ op_reply = create_cib_reply(op, call_id, client_id, call_options, rc,
+ the_cib);
} else if (process) {
time_t finished = 0;
time_t now = time(NULL);
int level = LOG_INFO;
const char *section = crm_element_value(request, F_CIB_SECTION);
- rc = cib_process_command(request, &op_reply, &result_diff, privileged);
+ rc = cib_process_command(request, operation, &op_reply, &result_diff,
+ privileged);
if (!is_update) {
level = LOG_TRACE;
} else if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
switch (rc) {
case pcmk_ok:
level = LOG_INFO;
break;
case -pcmk_err_old_data:
case -pcmk_err_diff_resync:
case -pcmk_err_diff_failed:
level = LOG_TRACE;
break;
default:
level = LOG_ERR;
}
} else if (rc != pcmk_ok) {
level = LOG_WARNING;
}
do_crm_log(level,
"Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)",
op, section ? section : "'all'", pcmk_strerror(rc), rc,
originator ? originator : "local", client_name, call_id,
the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN) : "0",
the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION) : "0",
the_cib ? crm_element_value(the_cib, XML_ATTR_NUMUPDATES) : "0");
finished = time(NULL);
if ((finished - now) > 3) {
crm_trace("%s operation took %lds to complete", op, (long)(finished - now));
crm_write_blackbox(0, NULL);
}
if (op_reply == NULL && (needs_reply || local_notify)) {
crm_err("Unexpected NULL reply to message");
crm_log_xml_err(request, "null reply");
needs_reply = FALSE;
local_notify = FALSE;
}
}
if (is_update && !cib_legacy_mode()) {
crm_trace("Completed pre-sync update from %s/%s/%s%s",
originator ? originator : "local", client_name, call_id,
local_notify?" with local notification":"");
} else if (!needs_reply || stand_alone) {
// This was a non-originating secondary update
crm_trace("Completed update as secondary");
} else if (cib_legacy_mode() &&
rc == pcmk_ok && result_diff != NULL && !(call_options & cib_inhibit_bcast)) {
gboolean broadcast = FALSE;
cib_local_bcast_num++;
crm_xml_add_int(request, F_CIB_LOCAL_NOTIFY_ID, cib_local_bcast_num);
broadcast = send_peer_reply(request, result_diff, originator, TRUE);
if (broadcast && client_id && local_notify && op_reply) {
/* If we have been asked to sync the reply,
* and a bcast msg has gone out, we queue the local notify
* until we know the bcast message has been received */
local_notify = FALSE;
crm_trace("Queuing local %ssync notification for %s",
(call_options & cib_sync_call) ? "" : "a-", client_id);
queue_local_notify(op_reply, client_id,
pcmk_is_set(call_options, cib_sync_call),
(cib_client == NULL));
op_reply = NULL; /* the reply is queued, so don't free here */
}
- } else if (call_options & cib_discard_reply) {
- crm_trace("Caller isn't interested in reply");
+ } else if ((cib_client == NULL)
+ && !pcmk_is_set(call_options, cib_discard_reply)) {
- } else if (cib_client == NULL) {
if (is_update == FALSE || result_diff == NULL) {
crm_trace("Request not broadcast: R/O call");
} else if (call_options & cib_inhibit_bcast) {
crm_trace("Request not broadcast: inhibited");
} else if (rc != pcmk_ok) {
crm_trace("Request not broadcast: call failed: %s", pcmk_strerror(rc));
} else {
crm_trace("Directing reply to %s", originator);
}
send_peer_reply(op_reply, result_diff, originator, FALSE);
}
if (local_notify && client_id) {
crm_trace("Performing local %ssync notification for %s",
(pcmk_is_set(call_options, cib_sync_call)? "" : "a"),
client_id);
if (process == FALSE) {
do_local_notify(request, client_id,
pcmk_is_set(call_options, cib_sync_call),
(cib_client == NULL));
} else {
do_local_notify(op_reply, client_id,
pcmk_is_set(call_options, cib_sync_call),
(cib_client == NULL));
}
}
free_xml(op_reply);
free_xml(result_diff);
return;
}
static char *
calculate_section_digest(const char *xpath, xmlNode * xml_obj)
{
xmlNode *xml_section = NULL;
if (xml_obj == NULL) {
return NULL;
}
xml_section = get_xpath_object(xpath, xml_obj, LOG_TRACE);
if (xml_section == NULL) {
return NULL;
}
return calculate_xml_versioned_digest(xml_section, FALSE, TRUE, CRM_FEATURE_SET);
}
+#define XPATH_CONFIG "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
+#define XPATH_NODES XPATH_CONFIG "/" XML_CIB_TAG_NODES
+#define XPATH_ALERTS XPATH_CONFIG "/" XML_CIB_TAG_ALERTS
+#define XPATH_STATUS "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS
+
+/*!
+ * \internal
+ * \brief Calculate digests of CIB sections
+ *
+ * \param[in] cib CIB to get digests from
+ * \param[out] digests Where to store digests
+ *
+ * \note The caller is responsible for freeing the output argument's members
+ * using \p free_digests().
+ */
+static void
+get_digests(xmlNode *cib, struct digest_data *digests)
+{
+ digests->nodes = calculate_section_digest(XPATH_NODES, cib);
+ digests->alerts = calculate_section_digest(XPATH_ALERTS, cib);
+ digests->status = calculate_section_digest(XPATH_STATUS, cib);
+}
+
+/*!
+ * \internal
+ * \brief Determine which CIB sections were changed by an operation
+ *
+ * \param[in] before Digests before the operation
+ * \param[in] after Digests after the operation
+ *
+ * \return Group of <tt>enum cib_change_section_info</tt> flags indicating which
+ * sections have changed
+ */
+static uint32_t
+get_change_sections(const struct digest_data *before,
+ const struct digest_data *after)
+{
+ uint32_t change_sections = cib_change_section_none;
+
+ if (!pcmk__str_eq(before->nodes, after->nodes, pcmk__str_none)) {
+ pcmk__set_change_section(change_sections, cib_change_section_nodes);
+ }
+ if (!pcmk__str_eq(before->alerts, after->alerts, pcmk__str_none)) {
+ pcmk__set_change_section(change_sections, cib_change_section_alerts);
+ }
+ if (!pcmk__str_eq(before->status, after->status, pcmk__str_none)) {
+ pcmk__set_change_section(change_sections, cib_change_section_status);
+ }
+
+ return change_sections;
+}
+
+/*!
+ * \internal
+ * \brief Check whether a CIB replace notification should be sent
+ *
+ * A CIB replace notification should be sent after an operation if the operation
+ * is of an appropriate type, notifications are not disabled, and any of certain
+ * CIB sections has changed.
+ *
+ * \param[in] before_digests Digests before operation
+ * \param[in] after_cib Result CIB after operation
+ * \param[in] operation CIB operation
+ * \param[in] call_options Group of <tt>enum cib_call_options</tt> flags
+ * \param[out] change_sections Group of <tt>enum cib_change_section_info</tt>
+ * flags indicating which sections have changed
+ *
+ * \return \p true if a replace notification should be sent, or \p false
+ * otherwise
+ */
+static bool
+should_replace_notify(const struct digest_data *before_digests,
+ xmlNode *after_cib, const cib_operation_t *operation,
+ int call_options, uint32_t *change_sections)
+{
+ struct digest_data after_digests = { 0, };
+
+ *change_sections = cib_change_section_none;
+
+ if (!pcmk_is_set(operation->flags, cib_op_attr_replaces)
+ || pcmk_is_set(call_options, cib_inhibit_notify)) {
+ return false;
+ }
+
+ get_digests(after_cib, &after_digests);
+ crm_trace("after-digest %s:%s:%s",
+ pcmk__s(after_digests.nodes, "(null)"),
+ pcmk__s(after_digests.alerts, "(null)"),
+ pcmk__s(after_digests.status, "(null)"));
+
+ *change_sections = get_change_sections(before_digests, &after_digests);
+ free_digests(&after_digests);
+
+ return (*change_sections != cib_change_section_none);
+}
+
// v1 and v2 patch formats
#define XPATH_CONFIG_CHANGE \
"//" XML_CIB_TAG_CRMCONFIG " | " \
"//" XML_DIFF_CHANGE \
"[contains(@" XML_DIFF_PATH ",'/" XML_CIB_TAG_CRMCONFIG "/')]"
static bool
contains_config_change(xmlNode *diff)
{
bool changed = false;
if (diff) {
xmlXPathObject *xpathObj = xpath_search(diff, XPATH_CONFIG_CHANGE);
if (numXpathResults(xpathObj) > 0) {
changed = true;
}
freeXpathObject(xpathObj);
}
return changed;
}
static int
-cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gboolean privileged)
+cib_process_command(xmlNode *request, const cib_operation_t *operation,
+ xmlNode **reply, xmlNode **cib_diff, bool privileged)
{
xmlNode *input = NULL;
xmlNode *output = NULL;
xmlNode *result_cib = NULL;
- xmlNode *current_cib = NULL;
int call_options = 0;
const char *op = NULL;
const char *section = NULL;
const char *call_id = crm_element_value(request, F_CIB_CALLID);
const char *client_id = crm_element_value(request, F_CIB_CLIENTID);
const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
const char *origin = crm_element_value(request, F_ORIG);
- const cib_operation_t *operation = NULL;
-
int rc = pcmk_ok;
- gboolean send_r_notify = FALSE;
gboolean config_changed = FALSE;
gboolean manage_counters = TRUE;
static mainloop_timer_t *digest_timer = NULL;
- char *current_nodes_digest = NULL;
- char *current_alerts_digest = NULL;
- char *current_status_digest = NULL;
- uint32_t change_section = cib_change_section_nodes
- |cib_change_section_alerts
- |cib_change_section_status;
+ struct digest_data before_digests = { 0, };
CRM_ASSERT(cib_status == pcmk_ok);
if(digest_timer == NULL) {
digest_timer = mainloop_timer_add("digester", 5000, FALSE, cib_digester_cb, NULL);
}
*reply = NULL;
*cib_diff = NULL;
- current_cib = the_cib;
/* Start processing the request... */
op = crm_element_value(request, F_CIB_OPERATION);
crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
- rc = cib_get_operation(op, &operation);
- if (rc != pcmk_ok) {
- crm_trace("Failed to get operation: %s", pcmk_strerror(rc));
- goto done;
- }
-
if (!privileged && pcmk_is_set(operation->flags, cib_op_attr_privileged)) {
rc = -EACCES;
crm_trace("Failed due to lack of privileges: %s", pcmk_strerror(rc));
goto done;
}
rc = operation->prepare(request, &input, §ion);
if (rc != pcmk_ok) {
crm_trace("Failed to prepare operation: %s", pcmk_strerror(rc));
goto done;
}
if (!pcmk_is_set(operation->flags, cib_op_attr_modifies)) {
rc = cib_perform_op(op, call_options, operation->fn, TRUE, section,
- request, input, FALSE, &config_changed, current_cib,
+ request, input, FALSE, &config_changed, the_cib,
&result_cib, NULL, &output);
CRM_CHECK(result_cib == NULL, free_xml(result_cib));
goto done;
}
- /* Handle a valid write action */
+ /* @COMPAT: Handle a valid write action (legacy)
+ *
+ * @TODO: Re-evaluate whether this is all truly legacy. The cib_force_diff
+ * portion is. However, F_CIB_GLOBAL_UPDATE may be set by a sync operation
+ * even in non-legacy mode, and manage_counters tells xml_create_patchset()
+ * whether to update version/epoch info.
+ */
if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
- /* legacy code */
manage_counters = FALSE;
cib__set_call_options(call_options, "call", cib_force_diff);
crm_trace("Global update detected");
- CRM_LOG_ASSERT(pcmk__str_any_of(operation->name,
+ CRM_LOG_ASSERT(pcmk__str_any_of(op,
PCMK__CIB_REQUEST_APPLY_PATCH,
PCMK__CIB_REQUEST_REPLACE,
NULL));
}
ping_modified_since = TRUE;
if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
crm_trace("Skipping update: inhibit broadcast");
manage_counters = FALSE;
}
if (!pcmk_is_set(call_options, cib_dryrun)
&& pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
// Copying large CIBs accounts for a huge percentage of our CIB usage
cib__set_call_options(call_options, "call", cib_zero_copy);
} else {
cib__clear_call_options(call_options, "call", cib_zero_copy);
}
-#define XPATH_CONFIG "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
-#define XPATH_NODES XPATH_CONFIG "/" XML_CIB_TAG_NODES
-#define XPATH_ALERTS XPATH_CONFIG "/" XML_CIB_TAG_ALERTS
-#define XPATH_STATUS "//" XML_TAG_CIB "/" XML_CIB_TAG_STATUS
-
- // Calculate the hash value of the section before the change
- if (!pcmk_any_flags_set(call_options, cib_dryrun|cib_inhibit_notify)
- && pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) {
+ // Calculate the digests of relevant sections before the operation
+ if (pcmk_is_set(operation->flags, cib_op_attr_replaces)
+ && !pcmk_any_flags_set(call_options, cib_dryrun|cib_inhibit_notify)) {
- current_nodes_digest = calculate_section_digest(XPATH_NODES,
- current_cib);
- current_alerts_digest = calculate_section_digest(XPATH_ALERTS,
- current_cib);
- current_status_digest = calculate_section_digest(XPATH_STATUS,
- current_cib);
- crm_trace("current-digest %s:%s:%s", current_nodes_digest,
- current_alerts_digest, current_status_digest);
+ get_digests(the_cib, &before_digests);
+ crm_trace("before-digest %s:%s:%s",
+ pcmk__s(before_digests.nodes, "(null)"),
+ pcmk__s(before_digests.alerts, "(null)"),
+ pcmk__s(before_digests.status, "(null)"));
}
// result_cib must not be modified after cib_perform_op() returns
rc = cib_perform_op(op, call_options, operation->fn, FALSE, section,
request, input, manage_counters, &config_changed,
- current_cib, &result_cib, cib_diff, &output);
+ the_cib, &result_cib, cib_diff, &output);
+ // @COMPAT: Legacy code
if (!manage_counters) {
int format = 1;
- /* Legacy code
- * If the diff is NULL at this point, it's because nothing changed
- */
+ // If the diff is NULL at this point, it's because nothing changed
if (*cib_diff != NULL) {
crm_element_value_int(*cib_diff, "format", &format);
}
if (format == 1) {
config_changed = cib__config_changed_v1(NULL, NULL, cib_diff);
}
}
- /* Always write to disk for successful replace and upgrade ops. This also
+ /* Always write to disk for successful ops with the flag set. This also
* negates the need to detect ordering changes.
*/
if ((rc == pcmk_ok)
- && pcmk__str_any_of(op,
- PCMK__CIB_REQUEST_REPLACE,
- PCMK__CIB_REQUEST_UPGRADE,
- NULL)) {
+ && pcmk_is_set(operation->flags, cib_op_attr_writes_through)) {
+
config_changed = TRUE;
}
if (rc == pcmk_ok && !pcmk_is_set(call_options, cib_dryrun)) {
+ uint32_t change_sections = cib_change_section_none;
+
crm_trace("Activating %s->%s%s%s",
- crm_element_value(current_cib, XML_ATTR_NUMUPDATES),
+ crm_element_value(the_cib, XML_ATTR_NUMUPDATES),
crm_element_value(result_cib, XML_ATTR_NUMUPDATES),
(pcmk_is_set(call_options, cib_zero_copy)? " zero-copy" : ""),
(config_changed? " changed" : ""));
+
if (!pcmk_is_set(call_options, cib_zero_copy)) {
rc = activateCibXml(result_cib, config_changed, op);
- crm_trace("Activated %s (%d)",
- crm_element_value(current_cib, XML_ATTR_NUMUPDATES), rc);
+ if (rc != pcmk_ok) {
+ crm_err("Failed to activate new CIB: %s", pcmk_strerror(rc));
+ }
}
if ((rc == pcmk_ok) && contains_config_change(*cib_diff)) {
cib_read_config(config_hash, result_cib);
}
- if (!pcmk_is_set(call_options, cib_inhibit_notify)
- && pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) {
-
- char *result_nodes_digest = NULL;
- char *result_alerts_digest = NULL;
- char *result_status_digest = NULL;
-
- /* Calculate the hash value of the changed section. */
- result_nodes_digest = calculate_section_digest(XPATH_NODES,
- result_cib);
- result_alerts_digest = calculate_section_digest(XPATH_ALERTS,
- result_cib);
- result_status_digest = calculate_section_digest(XPATH_STATUS,
- result_cib);
- crm_trace("result-digest %s:%s:%s", result_nodes_digest,
- result_alerts_digest, result_status_digest);
-
- if (pcmk__str_eq(current_nodes_digest, result_nodes_digest,
- pcmk__str_none)) {
- change_section =
- pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE,
- "CIB change section",
- "change_section", change_section,
- cib_change_section_nodes, "nodes");
- }
-
- if (pcmk__str_eq(current_alerts_digest, result_alerts_digest,
- pcmk__str_none)) {
- change_section =
- pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE,
- "CIB change section",
- "change_section", change_section,
- cib_change_section_alerts, "alerts");
- }
-
- if (pcmk__str_eq(current_status_digest, result_status_digest,
- pcmk__str_none)) {
- change_section =
- pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE,
- "CIB change section",
- "change_section", change_section,
- cib_change_section_status, "status");
- }
-
- if (change_section != cib_change_section_none) {
- send_r_notify = TRUE;
- }
-
- free(result_nodes_digest);
- free(result_alerts_digest);
- free(result_status_digest);
+ if (should_replace_notify(&before_digests, result_cib, operation,
+ call_options, &change_sections)) {
- } else if (!pcmk_is_set(call_options, cib_inhibit_notify)
- && pcmk__str_eq(op, PCMK__CIB_REQUEST_ERASE,
- pcmk__str_none)) {
- send_r_notify = TRUE;
+ // @TODO: Should update argument be result_cib instead of the_cib?
+ cib_replace_notify(op, rc, call_id, client_id, client_name, origin,
+ the_cib, *cib_diff, change_sections);
}
mainloop_timer_stop(digest_timer);
mainloop_timer_start(digest_timer);
} else if (rc == -pcmk_err_schema_validation) {
CRM_ASSERT(!pcmk_is_set(call_options, cib_zero_copy));
if (output != NULL) {
crm_log_xml_info(output, "cib:output");
free_xml(output);
}
output = result_cib;
} else {
crm_trace("Not activating %d %d %s", rc,
pcmk_is_set(call_options, cib_dryrun),
crm_element_value(result_cib, XML_ATTR_NUMUPDATES));
if (!pcmk_is_set(call_options, cib_zero_copy)) {
free_xml(result_cib);
}
}
if ((call_options & (cib_inhibit_notify|cib_dryrun)) == 0) {
crm_trace("Sending notifications %d",
pcmk_is_set(call_options, cib_dryrun));
cib_diff_notify(op, rc, call_id, client_id, client_name, origin, input,
*cib_diff);
}
- if (send_r_notify) {
- cib_replace_notify(op, rc, call_id, client_id, client_name, origin,
- the_cib, *cib_diff, change_section);
- }
-
pcmk__output_set_log_level(logger_out, LOG_TRACE);
logger_out->message(logger_out, "xml-patchset", *cib_diff);
done:
if (!pcmk_is_set(call_options, cib_discard_reply) || cib_legacy_mode()) {
- const char *caller = crm_element_value(request, F_CIB_CLIENTID);
-
- *reply = create_xml_node(NULL, "cib-reply");
- crm_xml_add(*reply, F_TYPE, T_CIB);
- crm_xml_add(*reply, F_CIB_OPERATION, op);
- crm_xml_add(*reply, F_CIB_CALLID, call_id);
- crm_xml_add(*reply, F_CIB_CLIENTID, caller);
- crm_xml_add_int(*reply, F_CIB_CALLOPTS, call_options);
- crm_xml_add_int(*reply, F_CIB_RC, rc);
-
- if (output != NULL) {
- crm_trace("Attaching reply output");
- add_message_xml(*reply, F_CIB_CALLDATA, output);
- }
-
- crm_log_xml_explicit(*reply, "cib:reply");
+ *reply = create_cib_reply(op, call_id, client_id, call_options, rc,
+ output);
}
crm_trace("cleanup");
+ operation->cleanup(call_options, &input, &output);
- if (operation != NULL) {
- operation->cleanup(call_options, &input, &output);
- }
-
- free(current_nodes_digest);
- free(current_alerts_digest);
- free(current_status_digest);
+ free_digests(&before_digests);
crm_trace("done");
return rc;
}
void
cib_peer_callback(xmlNode * msg, void *private_data)
{
const char *reason = NULL;
const char *originator = crm_element_value(msg, F_ORIG);
if (cib_legacy_mode()
&& pcmk__str_eq(originator, OUR_NODENAME,
pcmk__str_casei|pcmk__str_null_matches)) {
/* message is from ourselves */
int bcast_id = 0;
if (!(crm_element_value_int(msg, F_CIB_LOCAL_NOTIFY_ID, &bcast_id))) {
check_local_notify(bcast_id);
}
return;
} else if (crm_peer_cache == NULL) {
reason = "membership not established";
goto bail;
}
if (crm_element_value(msg, F_CIB_CLIENTNAME) == NULL) {
crm_xml_add(msg, F_CIB_CLIENTNAME, originator);
}
/* crm_log_xml_trace(msg, "Peer[inbound]"); */
cib_process_request(msg, TRUE, NULL);
return;
bail:
if (reason) {
const char *seq = crm_element_value(msg, F_SEQ);
const char *op = crm_element_value(msg, F_CIB_OPERATION);
crm_warn("Discarding %s message (%s) from %s: %s", op, seq, originator, reason);
}
}
static gboolean
cib_force_exit(gpointer data)
{
crm_notice("Forcing exit!");
terminate_cib(__func__, CRM_EX_ERROR);
return FALSE;
}
static void
disconnect_remote_client(gpointer key, gpointer value, gpointer user_data)
{
pcmk__client_t *a_client = value;
crm_err("Can't disconnect client %s: Not implemented",
pcmk__client_name(a_client));
}
static void
initiate_exit(void)
{
int active = 0;
xmlNode *leaving = NULL;
active = crm_active_peers();
if (active < 2) {
terminate_cib(__func__, 0);
return;
}
crm_info("Sending disconnect notification to %d peers...", active);
leaving = create_xml_node(NULL, "exit-notification");
crm_xml_add(leaving, F_TYPE, "cib");
crm_xml_add(leaving, F_CIB_OPERATION, PCMK__CIB_REQUEST_SHUTDOWN);
send_cluster_message(NULL, crm_msg_cib, leaving, TRUE);
free_xml(leaving);
g_timeout_add(EXIT_ESCALATION_MS, cib_force_exit, NULL);
}
void
cib_shutdown(int nsig)
{
struct qb_ipcs_stats srv_stats;
if (cib_shutdown_flag == FALSE) {
int disconnects = 0;
qb_ipcs_connection_t *c = NULL;
cib_shutdown_flag = TRUE;
c = qb_ipcs_connection_first_get(ipcs_rw);
while (c != NULL) {
qb_ipcs_connection_t *last = c;
c = qb_ipcs_connection_next_get(ipcs_rw, last);
crm_debug("Disconnecting r/w client %p...", last);
qb_ipcs_disconnect(last);
qb_ipcs_connection_unref(last);
disconnects++;
}
c = qb_ipcs_connection_first_get(ipcs_ro);
while (c != NULL) {
qb_ipcs_connection_t *last = c;
c = qb_ipcs_connection_next_get(ipcs_ro, last);
crm_debug("Disconnecting r/o client %p...", last);
qb_ipcs_disconnect(last);
qb_ipcs_connection_unref(last);
disconnects++;
}
c = qb_ipcs_connection_first_get(ipcs_shm);
while (c != NULL) {
qb_ipcs_connection_t *last = c;
c = qb_ipcs_connection_next_get(ipcs_shm, last);
crm_debug("Disconnecting non-blocking r/w client %p...", last);
qb_ipcs_disconnect(last);
qb_ipcs_connection_unref(last);
disconnects++;
}
disconnects += pcmk__ipc_client_count();
crm_debug("Disconnecting %d remote clients", pcmk__ipc_client_count());
pcmk__foreach_ipc_client(disconnect_remote_client, NULL);
crm_info("Disconnected %d clients", disconnects);
}
qb_ipcs_stats_get(ipcs_rw, &srv_stats, QB_FALSE);
if (pcmk__ipc_client_count() == 0) {
crm_info("All clients disconnected (%d)", srv_stats.active_connections);
initiate_exit();
} else {
crm_info("Waiting on %d clients to disconnect (%d)",
pcmk__ipc_client_count(), srv_stats.active_connections);
}
}
extern int remote_fd;
extern int remote_tls_fd;
/*!
* \internal
* \brief Close remote sockets, free the global CIB and quit
*
* \param[in] caller Name of calling function (for log message)
* \param[in] fast If -1, skip disconnect; if positive, exit that
*/
void
terminate_cib(const char *caller, int fast)
{
crm_info("%s: Exiting%s...", caller,
(fast > 0)? " fast" : mainloop ? " from mainloop" : "");
if (remote_fd > 0) {
close(remote_fd);
remote_fd = 0;
}
if (remote_tls_fd > 0) {
close(remote_tls_fd);
remote_tls_fd = 0;
}
uninitializeCib();
if (logger_out != NULL) {
logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
pcmk__output_free(logger_out);
logger_out = NULL;
}
if (fast > 0) {
/* Quit fast on error */
pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
crm_exit(fast);
} else if ((mainloop != NULL) && g_main_loop_is_running(mainloop)) {
/* Quit via returning from the main loop. If fast == -1, we skip the
* disconnect here, and it will be done when the main loop returns
* (this allows the peer status callback to avoid messing with the
* peer caches).
*/
if (fast == 0) {
crm_cluster_disconnect(crm_cluster);
}
g_main_loop_quit(mainloop);
} else {
/* Quit via clean exit. Even the peer status callback can disconnect
* here, because we're not returning control to the caller. */
crm_cluster_disconnect(crm_cluster);
pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
crm_exit(CRM_EX_OK);
}
}
diff --git a/daemons/based/based_common.c b/daemons/based/based_common.c
index 1b1bc025f4..a263b7f06a 100644
--- a/daemons/based/based_common.c
+++ b/daemons/based/based_common.c
@@ -1,307 +1,263 @@
/*
* Copyright 2008-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 <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster.h>
#include <crm/common/xml.h>
#include <pacemaker-based.h>
gboolean stand_alone = FALSE;
extern int cib_perform_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff,
gboolean privileged);
static xmlNode *
cib_prepare_common(xmlNode * root, const char *section)
{
xmlNode *data = NULL;
/* extract the CIB from the fragment */
if (root == NULL) {
return NULL;
} else if (pcmk__strcase_any_of(crm_element_name(root), XML_TAG_FRAGMENT,
F_CRM_DATA, F_CIB_CALLDATA, NULL)) {
data = first_named_child(root, XML_TAG_CIB);
} else {
data = root;
}
/* grab the section specified for the command */
if (section != NULL && data != NULL && pcmk__str_eq(crm_element_name(data), XML_TAG_CIB, pcmk__str_none)) {
data = pcmk_find_cib_element(data, section);
}
/* crm_log_xml_trace(root, "cib:input"); */
return data;
}
static int
cib_prepare_none(xmlNode * request, xmlNode ** data, const char **section)
{
*data = NULL;
*section = crm_element_value(request, F_CIB_SECTION);
return pcmk_ok;
}
static int
cib_prepare_data(xmlNode * request, xmlNode ** data, const char **section)
{
xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA);
*section = crm_element_value(request, F_CIB_SECTION);
*data = cib_prepare_common(input_fragment, *section);
/* crm_log_xml_debug(*data, "data"); */
return pcmk_ok;
}
static int
cib_prepare_sync(xmlNode * request, xmlNode ** data, const char **section)
{
*data = NULL;
*section = crm_element_value(request, F_CIB_SECTION);
return pcmk_ok;
}
static int
cib_prepare_diff(xmlNode * request, xmlNode ** data, const char **section)
{
xmlNode *input_fragment = NULL;
*data = NULL;
*section = NULL;
if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
input_fragment = get_message_xml(request, F_CIB_UPDATE_DIFF);
} else {
input_fragment = get_message_xml(request, F_CIB_CALLDATA);
}
CRM_CHECK(input_fragment != NULL, crm_log_xml_warn(request, "no input"));
*data = cib_prepare_common(input_fragment, NULL);
return pcmk_ok;
}
static int
cib_cleanup_query(int options, xmlNode ** data, xmlNode ** output)
{
CRM_LOG_ASSERT(*data == NULL);
if (*output != the_cib) {
free_xml(*output);
}
return pcmk_ok;
}
static int
cib_cleanup_data(int options, xmlNode ** data, xmlNode ** output)
{
free_xml(*output);
*data = NULL;
return pcmk_ok;
}
static int
cib_cleanup_output(int options, xmlNode ** data, xmlNode ** output)
{
free_xml(*output);
return pcmk_ok;
}
static int
cib_cleanup_none(int options, xmlNode ** data, xmlNode ** output)
{
CRM_LOG_ASSERT(*data == NULL);
CRM_LOG_ASSERT(*output == NULL);
return pcmk_ok;
}
static const cib_operation_t cib_server_ops[] = {
{
PCMK__CIB_REQUEST_QUERY,
cib_op_attr_none,
cib_prepare_none, cib_cleanup_query, cib_process_query
},
{
PCMK__CIB_REQUEST_MODIFY,
cib_op_attr_modifies|cib_op_attr_privileged,
cib_prepare_data, cib_cleanup_data, cib_process_modify
},
{
PCMK__CIB_REQUEST_APPLY_PATCH,
cib_op_attr_modifies|cib_op_attr_privileged,
cib_prepare_diff, cib_cleanup_data, cib_server_process_diff
},
{
PCMK__CIB_REQUEST_REPLACE,
- cib_op_attr_modifies|cib_op_attr_privileged,
+ cib_op_attr_modifies
+ |cib_op_attr_privileged
+ |cib_op_attr_replaces
+ |cib_op_attr_writes_through,
cib_prepare_data, cib_cleanup_data, cib_process_replace_svr
},
{
PCMK__CIB_REQUEST_CREATE,
cib_op_attr_modifies|cib_op_attr_privileged,
cib_prepare_data, cib_cleanup_data, cib_process_create
},
{
PCMK__CIB_REQUEST_DELETE,
cib_op_attr_modifies|cib_op_attr_privileged,
cib_prepare_data, cib_cleanup_data, cib_process_delete
},
{
PCMK__CIB_REQUEST_SYNC_TO_ALL,
cib_op_attr_privileged,
cib_prepare_sync, cib_cleanup_none, cib_process_sync
},
{
PCMK__CIB_REQUEST_BUMP,
cib_op_attr_modifies|cib_op_attr_privileged,
cib_prepare_none, cib_cleanup_output, cib_process_bump
},
{
PCMK__CIB_REQUEST_ERASE,
- cib_op_attr_modifies|cib_op_attr_privileged,
+ cib_op_attr_modifies|cib_op_attr_privileged|cib_op_attr_replaces,
cib_prepare_none, cib_cleanup_output, cib_process_erase
},
{
PCMK__CIB_REQUEST_NOOP,
cib_op_attr_none,
cib_prepare_none, cib_cleanup_none, cib_process_noop
},
{
PCMK__CIB_REQUEST_ABS_DELETE,
cib_op_attr_modifies|cib_op_attr_privileged,
cib_prepare_data, cib_cleanup_data, cib_process_delete_absolute
},
{
PCMK__CIB_REQUEST_UPGRADE,
- cib_op_attr_modifies|cib_op_attr_privileged,
+ cib_op_attr_modifies|cib_op_attr_privileged|cib_op_attr_writes_through,
cib_prepare_none, cib_cleanup_output, cib_process_upgrade_server
},
{
PCMK__CIB_REQUEST_SECONDARY,
- cib_op_attr_privileged,
- cib_prepare_none, cib_cleanup_none, cib_process_readwrite
- },
- {
- PCMK__CIB_REQUEST_ALL_SECONDARY,
- cib_op_attr_privileged,
+ cib_op_attr_privileged|cib_op_attr_local,
cib_prepare_none, cib_cleanup_none, cib_process_readwrite
},
{
PCMK__CIB_REQUEST_SYNC_TO_ONE,
cib_op_attr_privileged,
cib_prepare_sync, cib_cleanup_none, cib_process_sync_one
},
{
+ // @COMPAT: Drop cib_op_attr_modifies when we drop legacy mode support
PCMK__CIB_REQUEST_PRIMARY,
- cib_op_attr_modifies|cib_op_attr_privileged,
+ cib_op_attr_modifies|cib_op_attr_privileged|cib_op_attr_local,
cib_prepare_data, cib_cleanup_data, cib_process_readwrite
},
{
PCMK__CIB_REQUEST_IS_PRIMARY,
cib_op_attr_privileged,
cib_prepare_none, cib_cleanup_none, cib_process_readwrite
},
{
PCMK__CIB_REQUEST_SHUTDOWN,
cib_op_attr_privileged,
cib_prepare_sync, cib_cleanup_none, cib_process_shutdown_req
},
{
CRM_OP_PING,
cib_op_attr_none,
cib_prepare_none, cib_cleanup_output, cib_process_ping
},
};
int
cib_get_operation(const char *op, const cib_operation_t **operation)
{
static GHashTable *operation_hash = NULL;
CRM_ASSERT((op != NULL) && (operation != NULL));
if (operation_hash == NULL) {
operation_hash = pcmk__strkey_table(NULL, NULL);
for (int lpc = 0; lpc < PCMK__NELEM(cib_server_ops); lpc++) {
const cib_operation_t *oper = &(cib_server_ops[lpc]);
g_hash_table_insert(operation_hash, (gpointer) oper->name,
(gpointer) oper);
}
}
*operation = g_hash_table_lookup(operation_hash, op);
if (*operation == NULL) {
crm_err("Operation %s is not valid", op);
return -EINVAL;
}
return pcmk_ok;
}
-
-xmlNode *
-cib_msg_copy(xmlNode *msg)
-{
- static const char *field_list[] = {
- F_XML_TAGNAME,
- F_TYPE,
- F_CIB_CLIENTID,
- F_CIB_CALLOPTS,
- F_CIB_CALLID,
- F_CIB_OPERATION,
- F_CIB_ISREPLY,
- F_CIB_SECTION,
- F_CIB_HOST,
- F_CIB_RC,
- F_CIB_DELEGATED,
- F_CIB_OBJID,
- F_CIB_OBJTYPE,
- F_CIB_EXISTING,
- F_CIB_SEENCOUNT,
- F_CIB_TIMEOUT,
- F_CIB_GLOBAL_UPDATE,
- F_CIB_CLIENTNAME,
- F_CIB_USER,
- F_CIB_NOTIFY_TYPE,
- F_CIB_NOTIFY_ACTIVATE
- };
-
- xmlNode *copy = create_xml_node(NULL, "copy");
-
- CRM_ASSERT(copy != NULL);
-
- for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
- const char *field = field_list[lpc];
- const char *value = crm_element_value(msg, field);
-
- if (value != NULL) {
- crm_xml_add(copy, field, value);
- }
- }
-
- return copy;
-}
diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c
index a1258eda05..c7f693509f 100644
--- a/daemons/based/based_messages.c
+++ b/daemons/based/based_messages.c
@@ -1,418 +1,451 @@
/*
* 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;
}
// @COMPAT: Remove when PCMK__CIB_REQUEST_NOOP is removed
int
cib_process_noop(const char *op, int options, const char *section, xmlNode *req,
xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
xmlNode **answer)
{
crm_trace("Processing \"%s\" event", op);
*answer = NULL;
return pcmk_ok;
}
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;
}
// @COMPAT: Remove when PCMK__CIB_REQUEST_ABS_DELETE is removed
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;
}
+static xmlNode *
+cib_msg_copy(xmlNode *msg)
+{
+ static const char *field_list[] = {
+ F_XML_TAGNAME,
+ F_TYPE,
+ F_CIB_CLIENTID,
+ F_CIB_CALLOPTS,
+ F_CIB_CALLID,
+ F_CIB_OPERATION,
+ F_CIB_ISREPLY,
+ F_CIB_SECTION,
+ F_CIB_HOST,
+ F_CIB_RC,
+ F_CIB_DELEGATED,
+ F_CIB_OBJID,
+ F_CIB_OBJTYPE,
+ F_CIB_EXISTING,
+ F_CIB_SEENCOUNT,
+ F_CIB_TIMEOUT,
+ F_CIB_GLOBAL_UPDATE,
+ F_CIB_CLIENTNAME,
+ F_CIB_USER,
+ F_CIB_NOTIFY_TYPE,
+ F_CIB_NOTIFY_ACTIVATE
+ };
+
+ xmlNode *copy = create_xml_node(NULL, "copy");
+
+ CRM_ASSERT(copy != NULL);
+
+ for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
+ const char *field = field_list[lpc];
+ const char *value = crm_element_value(msg, field);
+
+ if (value != NULL) {
+ crm_xml_add(copy, field, value);
+ }
+ }
+
+ return copy;
+}
+
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);
- CRM_CHECK(replace_request != NULL, return -EINVAL);
+ CRM_CHECK(all || (host != 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
- */
+ replace_request = cib_msg_copy(request);
+
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;
}
diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h
index 5a3f373c63..c705958f63 100644
--- a/daemons/based/pacemaker-based.h
+++ b/daemons/based/pacemaker-based.h
@@ -1,152 +1,154 @@
/*
* 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 Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PACEMAKER_BASED__H
# define PACEMAKER_BASED__H
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <glib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include <crm/cib/internal.h>
#ifdef HAVE_GNUTLS_GNUTLS_H
# include <gnutls/gnutls.h>
#endif
// CIB-specific client flags
enum cib_client_flags {
// Notifications
cib_notify_pre = (UINT64_C(1) << 0),
cib_notify_post = (UINT64_C(1) << 1),
cib_notify_replace = (UINT64_C(1) << 2),
cib_notify_confirm = (UINT64_C(1) << 3),
cib_notify_diff = (UINT64_C(1) << 4),
// Whether client is another cluster daemon
cib_is_daemon = (UINT64_C(1) << 12),
};
/*!
* \internal
* \enum cib_op_attr
* \brief Bit flags for CIB operation attributes
*/
enum cib_op_attr {
- cib_op_attr_none = 0, //!< No special attributes
- cib_op_attr_modifies = (1 << 1), //!< Modifies CIB
- cib_op_attr_privileged = (1 << 2), //!< Requires privileges
+ cib_op_attr_none = 0, //!< No special attributes
+ cib_op_attr_modifies = (1 << 1), //!< Modifies CIB
+ cib_op_attr_privileged = (1 << 2), //!< Requires privileges
+ cib_op_attr_local = (1 << 3), //!< Must only be processed locally
+ cib_op_attr_replaces = (1 << 4), //!< Replaces CIB
+ cib_op_attr_writes_through = (1 << 5), //!< Writes to disk on success
};
typedef struct cib_operation_s {
const char *name;
uint32_t flags; //!< Group of <tt>enum cib_op_attr</tt> flags
int (*prepare) (xmlNode *, xmlNode **, const char **);
int (*cleanup) (int, xmlNode **, xmlNode **);
cib_op_t fn;
} cib_operation_t;
extern bool based_is_primary;
extern GHashTable *config_hash;
extern xmlNode *the_cib;
extern crm_trigger_t *cib_writer;
extern gboolean cib_writes_enabled;
extern GMainLoop *mainloop;
extern crm_cluster_t *crm_cluster;
extern GHashTable *local_notify_queue;
extern gboolean legacy_mode;
extern gboolean stand_alone;
extern gboolean cib_shutdown_flag;
extern gchar *cib_root;
extern int cib_status;
extern pcmk__output_t *logger_out;
extern struct qb_ipcs_service_handlers ipc_ro_callbacks;
extern struct qb_ipcs_service_handlers ipc_rw_callbacks;
extern qb_ipcs_service_t *ipcs_ro;
extern qb_ipcs_service_t *ipcs_rw;
extern qb_ipcs_service_t *ipcs_shm;
void cib_peer_callback(xmlNode *msg, void *private_data);
void cib_common_callback_worker(uint32_t id, uint32_t flags,
xmlNode *op_request, pcmk__client_t *cib_client,
gboolean privileged);
void cib_shutdown(int nsig);
void terminate_cib(const char *caller, int fast);
gboolean cib_legacy_mode(void);
gboolean uninitializeCib(void);
xmlNode *readCibXmlFile(const char *dir, const char *file,
gboolean discard_status);
int activateCibXml(xmlNode *doc, gboolean to_disk, const char *op);
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);
int cib_process_noop(const char *op, int options, const char *section,
xmlNode *req, xmlNode *input, xmlNode *existing_cib,
xmlNode **result_cib, xmlNode **answer);
int cib_process_ping(const char *op, int options, const char *section,
xmlNode *req, xmlNode *input, xmlNode *existing_cib,
xmlNode **result_cib, xmlNode **answer);
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 cib_process_replace_svr(const char *op, int options, const char *section,
xmlNode *req, xmlNode *input, xmlNode *existing_cib,
xmlNode **result_cib, xmlNode **answer);
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 cib_process_sync(const char *op, int options, const char *section,
xmlNode *req, xmlNode *input, xmlNode *existing_cib,
xmlNode **result_cib, xmlNode **answer);
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);
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);
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);
void send_sync_request(const char *host);
int sync_our_cib(xmlNode *request, gboolean all);
-xmlNode *cib_msg_copy(xmlNode *msg);
int cib_get_operation(const char *op, const cib_operation_t **operation);
void cib_diff_notify(const char *op, int result, const char *call_id,
const char *client_id, const char *client_name,
const char *origin, xmlNode *update, xmlNode *diff);
void cib_replace_notify(const char *op, int result, const char *call_id,
const char *client_id, const char *client_name,
const char *origin, xmlNode *update, xmlNode *diff,
uint32_t change_section);
static inline const char *
cib_config_lookup(const char *opt)
{
return g_hash_table_lookup(config_hash, opt);
}
#endif // PACEMAKER_BASED__H
diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h
index 2a44b9dcee..1434bfff77 100644
--- a/include/crm/cib/internal.h
+++ b/include/crm/cib/internal.h
@@ -1,264 +1,276 @@
/*
* 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 Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef CIB_INTERNAL__H
# define CIB_INTERNAL__H
# include <crm/cib.h>
# include <crm/common/ipc_internal.h>
# include <crm/common/output_internal.h>
// Request types for CIB manager IPC/CPG
#define PCMK__CIB_REQUEST_SECONDARY "cib_slave"
-#define PCMK__CIB_REQUEST_ALL_SECONDARY "cib_slave_all"
#define PCMK__CIB_REQUEST_PRIMARY "cib_master"
#define PCMK__CIB_REQUEST_SYNC_TO_ALL "cib_sync"
#define PCMK__CIB_REQUEST_SYNC_TO_ONE "cib_sync_one"
#define PCMK__CIB_REQUEST_IS_PRIMARY "cib_ismaster"
#define PCMK__CIB_REQUEST_BUMP "cib_bump"
#define PCMK__CIB_REQUEST_QUERY "cib_query"
#define PCMK__CIB_REQUEST_CREATE "cib_create"
#define PCMK__CIB_REQUEST_MODIFY "cib_modify"
#define PCMK__CIB_REQUEST_DELETE "cib_delete"
#define PCMK__CIB_REQUEST_ERASE "cib_erase"
#define PCMK__CIB_REQUEST_REPLACE "cib_replace"
#define PCMK__CIB_REQUEST_APPLY_PATCH "cib_apply_diff"
#define PCMK__CIB_REQUEST_UPGRADE "cib_upgrade"
#define PCMK__CIB_REQUEST_ABS_DELETE "cib_delete_alt"
#define PCMK__CIB_REQUEST_NOOP "noop"
#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req"
# define F_CIB_CLIENTID "cib_clientid"
# define F_CIB_CALLOPTS "cib_callopt"
# define F_CIB_CALLID "cib_callid"
# define F_CIB_CALLDATA "cib_calldata"
# define F_CIB_OPERATION "cib_op"
# define F_CIB_ISREPLY "cib_isreplyto"
# define F_CIB_SECTION "cib_section"
# define F_CIB_HOST "cib_host"
# define F_CIB_RC "cib_rc"
# define F_CIB_UPGRADE_RC "cib_upgrade_rc"
# define F_CIB_DELEGATED "cib_delegated_from"
# define F_CIB_OBJID "cib_object"
# define F_CIB_OBJTYPE "cib_object_type"
# define F_CIB_EXISTING "cib_existing_object"
# define F_CIB_SEENCOUNT "cib_seen"
# define F_CIB_TIMEOUT "cib_timeout"
# define F_CIB_UPDATE "cib_update"
# define F_CIB_GLOBAL_UPDATE "cib_update"
# define F_CIB_UPDATE_RESULT "cib_update_result"
# define F_CIB_CLIENTNAME "cib_clientname"
# define F_CIB_NOTIFY_TYPE "cib_notify_type"
# define F_CIB_NOTIFY_ACTIVATE "cib_notify_activate"
# define F_CIB_UPDATE_DIFF "cib_update_diff"
# define F_CIB_USER "cib_user"
# define F_CIB_LOCAL_NOTIFY_ID "cib_local_notify_id"
# define F_CIB_PING_ID "cib_ping_id"
# define F_CIB_SCHEMA_MAX "cib_schema_max"
# define F_CIB_CHANGE_SECTION "cib_change_section"
# define T_CIB "cib"
# define T_CIB_NOTIFY "cib_notify"
/* notify sub-types */
# define T_CIB_PRE_NOTIFY "cib_pre_notify"
# define T_CIB_POST_NOTIFY "cib_post_notify"
# define T_CIB_UPDATE_CONFIRM "cib_update_confirmation"
# define T_CIB_REPLACE_NOTIFY "cib_refresh_notify"
/*!
* \internal
* \enum cib_change_section_info
* \brief Flags to indicate which sections of the CIB have changed
*/
enum cib_change_section_info {
cib_change_section_none = 0, //!< No sections have changed
cib_change_section_nodes = (1 << 0), //!< The nodes section has changed
cib_change_section_alerts = (1 << 1), //!< The alerts section has changed
cib_change_section_status = (1 << 2), //!< The status section has changed
};
+/*!
+ * \internal
+ * \brief Set given <tt>enum cib_change_section_info</tt> flags
+ *
+ * \param[in,out] flags_orig Group of flags to update
+ * \param[in] flags_to_set Flags to clear from \p flags_orig
+ */
+#define pcmk__set_change_section(flags_orig, flags_to_set) do { \
+ flags_orig = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
+ "CIB change section", \
+ "change_section", flags_orig, \
+ flags_to_set, #flags_to_set); \
+ } while (0)
gboolean cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
int *_admin_epoch, int *_epoch, int *_updates);
gboolean cib_read_config(GHashTable * options, xmlNode * current_cib);
typedef struct cib_notify_client_s {
const char *event;
const char *obj_id; /* implement one day */
const char *obj_type; /* implement one day */
void (*callback) (const char *event, xmlNode * msg);
} cib_notify_client_t;
typedef struct cib_callback_client_s {
void (*callback) (xmlNode *, int, int, xmlNode *, void *);
const char *id;
void *user_data;
gboolean only_success;
struct timer_rec_s *timer;
void (*free_func)(void *);
} cib_callback_client_t;
struct timer_rec_s {
int call_id;
int timeout;
guint ref;
cib_t *cib;
};
#define cib__set_call_options(cib_call_opts, call_for, flags_to_set) do { \
cib_call_opts = pcmk__set_flags_as(__func__, __LINE__, \
LOG_TRACE, "CIB call", (call_for), (cib_call_opts), \
(flags_to_set), #flags_to_set); \
} while (0)
#define cib__clear_call_options(cib_call_opts, call_for, flags_to_clear) do { \
cib_call_opts = pcmk__clear_flags_as(__func__, __LINE__, \
LOG_TRACE, "CIB call", (call_for), (cib_call_opts), \
(flags_to_clear), #flags_to_clear); \
} while (0)
typedef int (*cib_op_t) (const char *, int, const char *, xmlNode *,
xmlNode *, xmlNode *, xmlNode **, xmlNode **);
cib_t *cib_new_variant(void);
int cib_perform_op(const char *op, int call_options, cib_op_t fn, gboolean is_query,
const char *section, xmlNode * req, xmlNode * input,
gboolean manage_counters, gboolean * config_changed,
xmlNode * current_cib, xmlNode ** result_cib, xmlNode ** diff,
xmlNode ** output);
xmlNode *cib_create_op(int call_id, const char *op, const char *host,
const char *section, xmlNode * data, int call_options,
const char *user_name);
void cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc);
void cib_native_notify(gpointer data, gpointer user_data);
int cib_process_query(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_erase(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_bump(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_create(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_modify(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_delete(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_diff(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
int cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer);
/*!
* \internal
* \brief Query or modify a CIB
*
* \param[in] op PCMK__CIB_REQUEST_* operation to be performed
* \param[in] options Flag set of \c cib_call_options
* \param[in] section XPath to query or modify
* \param[in] req unused
* \param[in] input Portion of CIB to modify (used with
* PCMK__CIB_REQUEST_CREATE,
* PCMK__CIB_REQUEST_MODIFY, and
* PCMK__CIB_REQUEST_REPLACE)
* \param[in,out] existing_cib Input CIB (used with PCMK__CIB_REQUEST_QUERY)
* \param[in,out] result_cib CIB copy to make changes in (used with
* PCMK__CIB_REQUEST_CREATE,
* PCMK__CIB_REQUEST_MODIFY,
* PCMK__CIB_REQUEST_DELETE, and
* PCMK__CIB_REQUEST_REPLACE)
* \param[out] answer Query result (used with PCMK__CIB_REQUEST_QUERY)
*
* \return Legacy Pacemaker return code
*/
int cib_process_xpath(const char *op, int options, const char *section,
const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
xmlNode **result_cib, xmlNode ** answer);
bool cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff);
int cib_internal_op(cib_t * cib, const char *op, const char *host,
const char *section, xmlNode * data,
xmlNode ** output_data, int call_options, const char *user_name);
int cib_file_read_and_verify(const char *filename, const char *sigfile,
xmlNode **root);
int cib_file_write_with_digest(xmlNode *cib_root, const char *cib_dirname,
const char *cib_filename);
void cib__set_output(cib_t *cib, pcmk__output_t *out);
cib_callback_client_t* cib__lookup_id (int call_id);
/*!
* \internal
* \brief Connect to, query, and optionally disconnect from the CIB
*
* Open a read-write connection to the CIB manager if an already connected
* client is not passed in. Then query the CIB and store the resulting XML.
* Finally, disconnect if the CIB connection isn't being returned to the caller.
*
* \param[in,out] out Output object (may be \p NULL)
* \param[in,out] cib If not \p NULL, where to store CIB connection
* \param[out] cib_object Where to store query result
*
* \return Standard Pacemaker return code
*
* \note If \p cib is not \p NULL, the caller is responsible for freeing \p *cib
* using \p cib_delete().
* \note If \p *cib points to an existing \p cib_t object, this function will
* reuse it instead of creating a new one. If the existing client is
* already connected, the connection will be reused, even if it's
* read-only.
*/
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object);
int cib__clean_up_connection(cib_t **cib);
int cib__update_node_attr(pcmk__output_t *out, cib_t *cib, int call_options,
const char *section, const char *node_uuid, const char *set_type,
const char *set_name, const char *attr_id, const char *attr_name,
const char *attr_value, const char *user_name,
const char *node_type);
int cib__get_node_attrs(pcmk__output_t *out, cib_t *cib, const char *section,
const char *node_uuid, const char *set_type, const char *set_name,
const char *attr_id, const char *attr_name, const char *user_name,
xmlNode **result);
int cib__delete_node_attr(pcmk__output_t *out, cib_t *cib, int options,
const char *section, const char *node_uuid, const char *set_type,
const char *set_name, const char *attr_id, const char *attr_name,
const char *attr_value, const char *user_name);
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 6:43 PM (2 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002776
Default Alt Text
(113 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment