diff options
Diffstat (limited to 'daemons/based/based_callbacks.c')
-rw-r--r-- | daemons/based/based_callbacks.c | 854 |
1 files changed, 430 insertions, 424 deletions
diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c index 3726caa..4fac222 100644 --- a/daemons/based/based_callbacks.c +++ b/daemons/based/based_callbacks.c @@ -20,6 +20,9 @@ #include <fcntl.h> #include <inttypes.h> // PRIu64 +#include <glib.h> +#include <libxml/tree.h> + #include <crm/crm.h> #include <crm/cib.h> #include <crm/msg_xml.h> @@ -31,7 +34,6 @@ #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; @@ -50,11 +52,10 @@ 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, + cib__op_fn_t op_function, xmlNode **reply, + xmlNode **cib_diff, bool privileged); static gboolean cib_common_callback(qb_ipcs_connection_t *c, void *data, size_t size, gboolean privileged); @@ -138,11 +139,130 @@ struct qb_ipcs_service_handlers ipc_rw_callbacks = { .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; +} + +static void +do_local_notify(const xmlNode *notify_src, const char *client_id, + bool sync_reply, bool 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); + } +} + 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); + int call_options = cib_none; + + crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options); + + /* Requests with cib_transaction set should not be sent to based directly + * (outside of a commit-transaction request) + */ + if (pcmk_is_set(call_options, cib_transaction)) { + return; + } if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) { if (flags & crm_ipc_client_response) { @@ -180,9 +300,6 @@ cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request, } 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; } @@ -354,9 +471,7 @@ process_ping_reply(xmlNode *reply) 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); + pcmk__log_xml_changes(LOG_INFO, remote_cib); crm_trace("End of differences"); } @@ -367,75 +482,6 @@ process_ping_reply(xmlNode *reply) } 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; @@ -448,7 +494,7 @@ local_notify_destroy_callback(gpointer data) static void check_local_notify(int bcast_id) { - cib_local_notify_t *notify = NULL; + const cib_local_notify_t *notify = NULL; if (!local_notify_queue) { return; @@ -483,13 +529,14 @@ queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_re } static void -parse_local_options_v1(const pcmk__client_t *cib_client, int call_type, - int call_options, const char *host, const char *op, - gboolean *local_notify, gboolean *needs_reply, - gboolean *process, gboolean *needs_forward) +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 (cib_op_modifies(call_type) - && !(call_options & cib_inhibit_bcast)) { + 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 { @@ -526,78 +573,87 @@ parse_local_options_v1(const pcmk__client_t *cib_client, int call_type, } static void -parse_local_options_v2(const pcmk__client_t *cib_client, int call_type, - int call_options, const char *host, const char *op, - gboolean *local_notify, gboolean *needs_reply, - gboolean *process, gboolean *needs_forward) +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 (cib_op_modifies(call_type)) { - 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 client %s", - op, pcmk__client_name(cib_client), - pcmk__s(host, "the primary instance")); - 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, int call_type, - int call_options, const char *host, const char *op, - gboolean *local_notify, gboolean *needs_reply, - gboolean *process, gboolean *needs_forward) +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, call_type, call_options, host, - op, local_notify, needs_reply, process, needs_forward); + 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, call_type, call_options, host, - op, local_notify, needs_reply, process, needs_forward); + parse_local_options_v2(cib_client, operation, call_options, host, + op, local_notify, needs_reply, process, + needs_forward); } } static gboolean -parse_peer_options_v1(int call_type, xmlNode * request, - gboolean * local_notify, gboolean * needs_reply, gboolean * process, - gboolean * needs_forward) +parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request, + gboolean *local_notify, gboolean *needs_reply, + gboolean *process) { const char *op = NULL; const char *host = NULL; @@ -620,7 +676,8 @@ parse_peer_options_v1(int call_type, xmlNode * request, } op = crm_element_value(request, F_CIB_OPERATION); - crm_trace("Processing %s request sent by %s", op, originator); + crm_trace("Processing legacy %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; @@ -693,9 +750,9 @@ parse_peer_options_v1(int call_type, xmlNode * request, } static gboolean -parse_peer_options_v2(int call_type, xmlNode * request, - gboolean * local_notify, gboolean * needs_reply, gboolean * process, - gboolean * needs_forward) +parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request, + gboolean *local_notify, gboolean *needs_reply, + gboolean *process) { const char *host = NULL; const char *delegated = crm_element_value(request, F_CIB_DELEGATED); @@ -705,6 +762,10 @@ parse_peer_options_v2(int call_type, xmlNode * request, gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei); + if (originator == NULL) { // Shouldn't be possible + originator = "peer"; + } + if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) { /* sync_our_cib() sets F_CIB_ISREPLY */ if (reply_to) { @@ -734,10 +795,10 @@ parse_peer_options_v2(int call_type, xmlNode * request, 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" : ""), + crm_trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s", + (is_reply? "reply" : "request"), (based_is_primary? "primary" : "secondary"), - (max? max : "none"), (upgrade_rc? upgrade_rc : "none")); + pcmk__s(max, "none"), pcmk__s(upgrade_rc, "none")); if (upgrade_rc != NULL) { // Our upgrade request was rejected by DC, notify clients of result @@ -752,7 +813,7 @@ parse_peer_options_v2(int call_type, xmlNode * request, goto skip_is_reply; } else { - // Ignore broadcast client requests when we're not DC + // Ignore broadcast client requests when we're not primary return FALSE; } @@ -762,22 +823,25 @@ parse_peer_options_v2(int call_type, xmlNode * request, legacy_mode = TRUE; return FALSE; - } else if (is_reply && cib_op_modifies(call_type)) { + } 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 */ - crm_debug("Legacy handling of %s message from %s", op, originator); *local_notify = FALSE; if (reply_to == NULL) { *process = TRUE; + } else { // Not possible? + crm_debug("Ignoring shutdown request from %s because reply_to=%s", + originator, reply_to); } return *process; } - if(is_reply) { - crm_trace("Handling %s reply sent from %s to local clients", op, originator); + if (is_reply) { + crm_trace("Will notify local clients for %s reply from %s", + op, originator); *process = FALSE; *needs_reply = FALSE; *local_notify = TRUE; @@ -797,62 +861,78 @@ parse_peer_options_v2(int call_type, xmlNode * request, 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); + crm_trace("Ignoring %s request intended for CIB manager 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)":""); + crm_trace("Processing %s request broadcast by %s call %s on %s " + "(local clients will%s be notified)", op, + pcmk__s(crm_element_value(request, F_CIB_CLIENTNAME), "client"), + pcmk__s(crm_element_value(request, F_CIB_CALLID), "without ID"), + originator, (*local_notify? "" : "not")); return TRUE; } static gboolean -parse_peer_options(int call_type, xmlNode * request, - gboolean * local_notify, gboolean * needs_reply, gboolean * process, - gboolean * needs_forward) +parse_peer_options(const cib__operation_t *operation, xmlNode *request, + gboolean *local_notify, gboolean *needs_reply, + 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( - call_type, request, local_notify, needs_reply, process, needs_forward); + return parse_peer_options_v1(operation, request, local_notify, + needs_reply, process); } else { - return parse_peer_options_v2( - call_type, request, local_notify, needs_reply, process, needs_forward); + return parse_peer_options_v2(operation, request, local_notify, + 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 @@ -861,9 +941,10 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb 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; @@ -878,7 +959,7 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb CRM_LOG_ASSERT(result_diff != NULL); digest = crm_element_value(result_diff, XML_ATTR_DIGEST); - crm_element_value_int(result_diff, "format", &format); + crm_element_value_int(result_diff, PCMK_XA_FORMAT, &format); cib_diff_version_details(result_diff, &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, @@ -919,12 +1000,14 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb * \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) + * + * \return Legacy Pacemaker return code */ -static void +int cib_process_request(xmlNode *request, gboolean privileged, const pcmk__client_t *cib_client) { - int call_type = 0; + // @TODO: Break into multiple smaller functions int call_options = 0; gboolean process = TRUE; // Whether to process request locally now @@ -946,12 +1029,16 @@ cib_process_request(xmlNode *request, gboolean privileged, 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; + cib__op_fn_t op_function = NULL; + crm_element_value_int(request, F_CIB_CALLOPTS, &call_options); if ((host != NULL) && (*host == '\0')) { host = NULL; } + // @TODO: Improve trace messages. Target is accurate only for legacy mode. if (host) { target = host; @@ -970,72 +1057,68 @@ cib_process_request(xmlNode *request, gboolean privileged, crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target); } - rc = cib_get_operation_id(op, &call_type); + rc = cib__get_operation(op, &operation); + rc = pcmk_rc2legacy(rc); if (rc != pcmk_ok) { /* TODO: construct error reply? */ crm_err("Pre-processing of command failed: %s", pcmk_strerror(rc)); - return; + return rc; + } + + op_function = based_get_op_function(operation); + if (op_function == NULL) { + crm_err("Operation %s not supported by CIB manager", op); + return -EOPNOTSUPP; } if (cib_client != NULL) { - parse_local_options(cib_client, call_type, call_options, host, op, - &local_notify, &needs_reply, &process, &needs_forward); + parse_local_options(cib_client, operation, call_options, host, op, + &local_notify, &needs_reply, &process, + &needs_forward); - } else if (parse_peer_options(call_type, request, &local_notify, - &needs_reply, &process, &needs_forward) == FALSE) { - return; + } else if (!parse_peer_options(operation, request, &local_notify, + &needs_reply, &process)) { + return rc; + } + + if (pcmk_is_set(call_options, cib_transaction)) { + /* All requests in a transaction are processed locally against a working + * CIB copy, and we don't notify for individual requests because the + * entire transaction is atomic. + * + * We still call the option parser functions above, for the sake of log + * messages and checking whether we're the target for peer requests. + */ + process = TRUE; + needs_reply = FALSE; + local_notify = FALSE; + needs_forward = FALSE; } - is_update = cib_op_modifies(call_type); + 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); - return; + forward_request(request); + return rc; } 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; @@ -1043,7 +1126,8 @@ cib_process_request(xmlNode *request, gboolean privileged, 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_function, &op_reply, + &result_diff, privileged); if (!is_update) { level = LOG_TRACE; @@ -1120,10 +1204,9 @@ cib_process_request(xmlNode *request, gboolean privileged, 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"); @@ -1158,24 +1241,51 @@ cib_process_request(xmlNode *request, gboolean privileged, free_xml(op_reply); free_xml(result_diff); - return; + return rc; } -static char * -calculate_section_digest(const char *xpath, xmlNode * xml_obj) +/*! + * \internal + * \brief Get a CIB operation's input from the request XML + * + * \param[in] request CIB request XML + * \param[in] type CIB operation type + * \param[out] section Where to store CIB section name + * + * \return Input XML for CIB operation + * + * \note If not \c NULL, the return value is a non-const pointer to part of + * \p request. The caller should not free it directly. + */ +static xmlNode * +prepare_input(const xmlNode *request, enum cib__op_type type, + const char **section) { - xmlNode *xml_section = NULL; + xmlNode *input = NULL; + + *section = NULL; + + switch (type) { + case cib__op_apply_patch: + if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) { + input = get_message_xml(request, F_CIB_UPDATE_DIFF); + } else { + input = get_message_xml(request, F_CIB_CALLDATA); + } + break; - if (xml_obj == NULL) { - return NULL; + default: + input = get_message_xml(request, F_CIB_CALLDATA); + *section = crm_element_value(request, F_CIB_SECTION); + break; } - xml_section = get_xpath_object(xpath, xml_obj, LOG_TRACE); - if (xml_section == NULL) { - return NULL; + // Grab the specified section + if ((*section != NULL) && pcmk__xe_is(input, XML_TAG_CIB)) { + input = pcmk_find_cib_element(input, *section); } - return calculate_xml_versioned_digest(xml_section, FALSE, TRUE, CRM_FEATURE_SET); + return input; } // v1 and v2 patch formats @@ -1201,14 +1311,14 @@ contains_config_change(xmlNode *diff) } static int -cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gboolean privileged) +cib_process_command(xmlNode *request, const cib__operation_t *operation, + cib__op_fn_t op_function, xmlNode **reply, + xmlNode **cib_diff, bool privileged) { xmlNode *input = NULL; xmlNode *output = NULL; xmlNode *result_cib = NULL; - xmlNode *current_cib = NULL; - int call_type = 0; int call_options = 0; const char *op = NULL; @@ -1216,24 +1326,15 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb 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 char *originator = crm_element_value(request, F_ORIG); int rc = pcmk_ok; - int rc2 = pcmk_ok; - gboolean send_r_notify = FALSE; - gboolean config_changed = FALSE; - gboolean manage_counters = TRUE; + bool config_changed = false; + bool 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; - CRM_ASSERT(cib_status == pcmk_ok); if(digest_timer == NULL) { @@ -1242,91 +1343,64 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb *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_id(op, &call_type); - if (rc == pcmk_ok && privileged == FALSE) { - rc = cib_op_can_run(call_type, call_options, privileged); + 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; } - rc2 = cib_op_prepare(call_type, request, &input, §ion); - if (rc == pcmk_ok) { - rc = rc2; - } + input = prepare_input(request, operation->type, §ion); - if (rc != pcmk_ok) { - crm_trace("Call setup failed: %s", pcmk_strerror(rc)); - goto done; - - } else if (cib_op_modifies(call_type) == FALSE) { - rc = cib_perform_op(op, call_options, cib_op_func(call_type), TRUE, - section, request, input, FALSE, &config_changed, - current_cib, &result_cib, NULL, &output); + if (!pcmk_is_set(operation->flags, cib__op_attr_modifies)) { + rc = cib_perform_op(op, call_options, op_function, true, section, + 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; + manage_counters = false; cib__set_call_options(call_options, "call", cib_force_diff); crm_trace("Global update detected"); - CRM_CHECK(call_type == 3 || call_type == 4, crm_err("Call type: %d", call_type); - crm_log_xml_err(request, "bad op")); + 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__str_eq(PCMK__CIB_REQUEST_REPLACE, op, pcmk__str_none)) { - 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); + manage_counters = false; } // result_cib must not be modified after cib_perform_op() returns - rc = cib_perform_op(op, call_options, cib_op_func(call_type), FALSE, - section, request, input, manage_counters, - &config_changed, current_cib, &result_cib, cib_diff, - &output); + rc = cib_perform_op(op, call_options, op_function, false, section, + request, input, manage_counters, &config_changed, + &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); + crm_element_value_int(*cib_diff, PCMK_XA_FORMAT, &format); } if (format == 1) { @@ -1334,92 +1408,60 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb } } - /* 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)) { - config_changed = TRUE; - } - - if (rc == pcmk_ok && !pcmk_is_set(call_options, cib_dryrun)) { - crm_trace("Activating %s->%s%s%s", - crm_element_value(current_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); - } + && pcmk_is_set(operation->flags, cib__op_attr_writes_through)) { - if ((rc == pcmk_ok) && contains_config_change(*cib_diff)) { - cib_read_config(config_hash, result_cib); - } + config_changed = true; + } - if (pcmk__str_eq(PCMK__CIB_REQUEST_REPLACE, op, 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 ((rc == pcmk_ok) + && !pcmk_any_flags_set(call_options, cib_dryrun|cib_transaction)) { - 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 (result_cib != the_cib) { + if (pcmk_is_set(operation->flags, cib__op_attr_writes_through)) { + config_changed = true; } - 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"); - } + crm_trace("Activating %s->%s%s", + crm_element_value(the_cib, XML_ATTR_NUMUPDATES), + crm_element_value(result_cib, XML_ATTR_NUMUPDATES), + (config_changed? " changed" : "")); - if (change_section != cib_change_section_none) { - send_r_notify = TRUE; + rc = activateCibXml(result_cib, config_changed, op); + if (rc != pcmk_ok) { + crm_err("Failed to activate new CIB: %s", pcmk_strerror(rc)); } - - free(result_nodes_digest); - free(result_alerts_digest); - free(result_status_digest); + } + + if ((rc == pcmk_ok) && contains_config_change(*cib_diff)) { + cib_read_config(config_hash, result_cib); + } - } else if (pcmk__str_eq(PCMK__CIB_REQUEST_ERASE, op, pcmk__str_none)) { - send_r_notify = TRUE; + /* @COMPAT Nodes older than feature set 3.19.0 don't support + * transactions. In a mixed-version cluster with nodes <3.19.0, we must + * sync the updated CIB, so that the older nodes receive the changes. + * Any node that has already applied the transaction will ignore the + * synced CIB. + * + * To ensure the updated CIB is synced from only one node, we sync it + * from the originator. + */ + if ((operation->type == cib__op_commit_transact) + && pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei) + && compare_version(crm_element_value(the_cib, XML_ATTR_CRM_VERSION), + "3.19.0") < 0) { + + sync_our_cib(request, TRUE); } 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)); + CRM_ASSERT(result_cib != the_cib); if (output != NULL) { crm_log_xml_info(output, "cib:output"); @@ -1432,61 +1474,31 @@ cib_process_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff, gb 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)) { + + if (result_cib != the_cib) { free_xml(result_cib); } } - if ((call_options & (cib_inhibit_notify|cib_dryrun)) == 0) { + if (!pcmk_any_flags_set(call_options, + cib_dryrun|cib_inhibit_notify|cib_transaction)) { 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); + cib_diff_notify(op, rc, call_id, client_id, client_name, originator, + 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); + pcmk__log_xml_patchset(LOG_TRACE, *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"); - - if (cib_op_modifies(call_type) == FALSE && output != current_cib) { + if (output != the_cib) { free_xml(output); - output = NULL; - } - - if (call_type >= 0) { - cib_op_cleanup(call_type, call_options, &input, &output); } - - free(current_nodes_digest); - free(current_alerts_digest); - free(current_status_digest); - crm_trace("done"); return rc; } @@ -1554,12 +1566,12 @@ initiate_exit(void) xmlNode *leaving = NULL; active = crm_active_peers(); - if (active < 2) { + if (active < 2) { // This is the last active node terminate_cib(__func__, 0); return; } - crm_info("Sending disconnect notification to %d peers...", active); + crm_info("Sending shutdown request to %d peers", active); leaving = create_xml_node(NULL, "exit-notification"); crm_xml_add(leaving, F_TYPE, "cib"); @@ -1664,12 +1676,6 @@ terminate_cib(const char *caller, int fast) 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); |