Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4624725
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
26 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/common/actions.c b/lib/common/actions.c
index 81cb29ada9..c21ed8d103 100644
--- a/lib/common/actions.c
+++ b/lib/common/actions.c
@@ -1,533 +1,473 @@
/*
* Copyright 2004-2024 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.
*/
#include <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/common/util.h>
/*!
* \brief Generate an operation key (RESOURCE_ACTION_INTERVAL)
*
* \param[in] rsc_id ID of resource being operated on
* \param[in] op_type Operation name
* \param[in] interval_ms Operation interval
*
* \return Newly allocated memory containing operation key as string
*
* \note This function asserts on errors, so it will never return NULL.
* The caller is responsible for freeing the result with free().
*/
char *
pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
{
CRM_ASSERT(rsc_id != NULL);
CRM_ASSERT(op_type != NULL);
return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
}
static inline gboolean
convert_interval(const char *s, guint *interval_ms)
{
unsigned long l;
errno = 0;
l = strtoul(s, NULL, 10);
if (errno != 0) {
return FALSE;
}
*interval_ms = (guint) l;
return TRUE;
}
/*!
* \internal
* \brief Check for underbar-separated substring match
*
* \param[in] key Overall string being checked
* \param[in] position Match before underbar at this \p key index
* \param[in] matches Substrings to match (may contain underbars)
*
* \return \p key index of underbar before any matching substring,
* or 0 if none
*/
static size_t
match_before(const char *key, size_t position, const char **matches)
{
for (int i = 0; matches[i] != NULL; ++i) {
const size_t match_len = strlen(matches[i]);
// Must have at least X_MATCH before position
if (position > (match_len + 1)) {
const size_t possible = position - match_len - 1;
if ((key[possible] == '_')
&& (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
return possible;
}
}
}
return 0;
}
gboolean
parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
{
guint local_interval_ms = 0;
const size_t key_len = (key == NULL)? 0 : strlen(key);
// Operation keys must be formatted as RSC_ACTION_INTERVAL
size_t action_underbar = 0; // Index in key of underbar before ACTION
size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
size_t possible = 0;
/* Underbar was a poor choice of separator since both RSC and ACTION can
* contain underbars. Here, list action names and name prefixes that can.
*/
const char *actions_with_underbars[] = {
PCMK_ACTION_MIGRATE_FROM,
PCMK_ACTION_MIGRATE_TO,
NULL
};
const char *action_prefixes_with_underbars[] = {
"pre_" PCMK_ACTION_NOTIFY,
"post_" PCMK_ACTION_NOTIFY,
"confirmed-pre_" PCMK_ACTION_NOTIFY,
"confirmed-post_" PCMK_ACTION_NOTIFY,
NULL,
};
// Initialize output variables in case of early return
if (rsc_id) {
*rsc_id = NULL;
}
if (op_type) {
*op_type = NULL;
}
if (interval_ms) {
*interval_ms = 0;
}
// RSC_ACTION_INTERVAL implies a minimum of 5 characters
if (key_len < 5) {
return FALSE;
}
// Find, parse, and validate interval
interval_underbar = key_len - 2;
while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
--interval_underbar;
}
if ((interval_underbar == 2)
|| !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
return FALSE;
}
// Find the base (OCF) action name, disregarding prefixes
action_underbar = match_before(key, interval_underbar,
actions_with_underbars);
if (action_underbar == 0) {
action_underbar = interval_underbar - 2;
while ((action_underbar > 0) && (key[action_underbar] != '_')) {
--action_underbar;
}
if (action_underbar == 0) {
return FALSE;
}
}
possible = match_before(key, action_underbar,
action_prefixes_with_underbars);
if (possible != 0) {
action_underbar = possible;
}
// Set output variables
if (rsc_id != NULL) {
*rsc_id = strndup(key, action_underbar);
CRM_ASSERT(*rsc_id != NULL);
}
if (op_type != NULL) {
*op_type = strndup(key + action_underbar + 1,
interval_underbar - action_underbar - 1);
CRM_ASSERT(*op_type != NULL);
}
if (interval_ms != NULL) {
*interval_ms = local_interval_ms;
}
return TRUE;
}
char *
pcmk__notify_key(const char *rsc_id, const char *notify_type,
const char *op_type)
{
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
CRM_CHECK(notify_type != NULL, return NULL);
return crm_strdup_printf("%s_%s_notify_%s_0",
rsc_id, notify_type, op_type);
}
/*!
* \brief Parse a transition magic string into its constituent parts
*
* \param[in] magic Magic string to parse (must be non-NULL)
* \param[out] uuid If non-NULL, where to store copy of parsed UUID
* \param[out] transition_id If non-NULL, where to store parsed transition ID
* \param[out] action_id If non-NULL, where to store parsed action ID
* \param[out] op_status If non-NULL, where to store parsed result status
* \param[out] op_rc If non-NULL, where to store parsed actual rc
* \param[out] target_rc If non-NULL, where to stored parsed target rc
*
* \return TRUE if key was valid, FALSE otherwise
* \note If uuid is supplied and this returns TRUE, the caller is responsible
* for freeing the memory for *uuid using free().
*/
gboolean
decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
int *op_status, int *op_rc, int *target_rc)
{
int res = 0;
char *key = NULL;
gboolean result = TRUE;
int local_op_status = -1;
int local_op_rc = -1;
CRM_CHECK(magic != NULL, return FALSE);
#ifdef HAVE_SSCANF_M
res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
#else
key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
CRM_ASSERT(key);
res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
#endif
if (res == EOF) {
crm_err("Could not decode transition information '%s': %s",
magic, pcmk_rc_str(errno));
result = FALSE;
} else if (res < 3) {
crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
magic, res);
result = FALSE;
} else {
if (op_status) {
*op_status = local_op_status;
}
if (op_rc) {
*op_rc = local_op_rc;
}
result = decode_transition_key(key, uuid, transition_id, action_id,
target_rc);
}
free(key);
return result;
}
char *
pcmk__transition_key(int transition_id, int action_id, int target_rc,
const char *node)
{
CRM_CHECK(node != NULL, return NULL);
return crm_strdup_printf("%d:%d:%d:%-*s",
action_id, transition_id, target_rc, 36, node);
}
/*!
* \brief Parse a transition key into its constituent parts
*
* \param[in] key Transition key to parse (must be non-NULL)
* \param[out] uuid If non-NULL, where to store copy of parsed UUID
* \param[out] transition_id If non-NULL, where to store parsed transition ID
* \param[out] action_id If non-NULL, where to store parsed action ID
* \param[out] target_rc If non-NULL, where to stored parsed target rc
*
* \return TRUE if key was valid, FALSE otherwise
* \note If uuid is supplied and this returns TRUE, the caller is responsible
* for freeing the memory for *uuid using free().
*/
gboolean
decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
int *target_rc)
{
int local_transition_id = -1;
int local_action_id = -1;
int local_target_rc = -1;
char local_uuid[37] = { '\0' };
// Initialize any supplied output arguments
if (uuid) {
*uuid = NULL;
}
if (transition_id) {
*transition_id = -1;
}
if (action_id) {
*action_id = -1;
}
if (target_rc) {
*target_rc = -1;
}
CRM_CHECK(key != NULL, return FALSE);
if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
&local_target_rc, local_uuid) != 4) {
crm_err("Invalid transition key '%s'", key);
return FALSE;
}
if (strlen(local_uuid) != 36) {
crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
}
if (uuid) {
*uuid = strdup(local_uuid);
CRM_ASSERT(*uuid);
}
if (transition_id) {
*transition_id = local_transition_id;
}
if (action_id) {
*action_id = local_action_id;
}
if (target_rc) {
*target_rc = local_target_rc;
}
return TRUE;
}
-// Return true if a is an attribute that should be filtered
-static bool
-should_filter_for_digest(xmlAttrPtr a, void *user_data)
-{
- if (strncmp((const char *) a->name, CRM_META "_",
- sizeof(CRM_META " ") - 1) == 0) {
- return true;
- }
- return pcmk__str_any_of((const char *) a->name,
- PCMK_XA_ID,
- PCMK_XA_CRM_FEATURE_SET,
- PCMK__XA_OP_DIGEST,
- PCMK__META_ON_NODE,
- PCMK__META_ON_NODE_UUID,
- "pcmk_external_ip",
- NULL);
-}
-
-/*!
- * \internal
- * \brief Remove XML attributes not needed for operation digest
- *
- * \param[in,out] param_set XML with operation parameters
- */
-void
-pcmk__filter_op_for_digest(xmlNode *param_set)
-{
- char *key = NULL;
- char *timeout = NULL;
- guint interval_ms = 0;
-
- if (param_set == NULL) {
- return;
- }
-
- /* Timeout is useful for recurring operation digests, so grab it before
- * removing meta-attributes
- */
- key = crm_meta_name(PCMK_META_INTERVAL);
- if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
- interval_ms = 0;
- }
- free(key);
- key = NULL;
- if (interval_ms != 0) {
- key = crm_meta_name(PCMK_META_TIMEOUT);
- timeout = crm_element_value_copy(param_set, key);
- }
-
- // Remove all CRM_meta_* attributes and certain other attributes
- pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
-
- // Add timeout back for recurring operation digests
- if (timeout != NULL) {
- crm_xml_add(param_set, key, timeout);
- }
- free(timeout);
- free(key);
-}
-
int
rsc_op_expected_rc(const lrmd_event_data_t *op)
{
int rc = 0;
if (op && op->user_data) {
decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
}
return rc;
}
gboolean
did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
{
switch (op->op_status) {
case PCMK_EXEC_CANCELLED:
case PCMK_EXEC_PENDING:
return FALSE;
case PCMK_EXEC_NOT_SUPPORTED:
case PCMK_EXEC_TIMEOUT:
case PCMK_EXEC_ERROR:
case PCMK_EXEC_NOT_CONNECTED:
case PCMK_EXEC_NO_FENCE_DEVICE:
case PCMK_EXEC_NO_SECRETS:
case PCMK_EXEC_INVALID:
return TRUE;
default:
if (target_rc != op->rc) {
return TRUE;
}
}
return FALSE;
}
/*!
* \brief Create a CIB XML element for an operation
*
* \param[in,out] parent If not NULL, make new XML node a child of this
* \param[in] prefix Generate an ID using this prefix
* \param[in] task Operation task to set
* \param[in] interval_spec Operation interval to set
* \param[in] timeout If not NULL, operation timeout to set
*
* \return New XML object on success, NULL otherwise
*/
xmlNode *
crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
const char *interval_spec, const char *timeout)
{
xmlNode *xml_op;
CRM_CHECK(prefix && task && interval_spec, return NULL);
xml_op = create_xml_node(parent, PCMK_XE_OP);
crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec);
crm_xml_add(xml_op, PCMK_XA_NAME, task);
if (timeout) {
crm_xml_add(xml_op, PCMK_META_TIMEOUT, timeout);
}
return xml_op;
}
/*!
* \brief Check whether an operation requires resource agent meta-data
*
* \param[in] rsc_class Resource agent class (or NULL to skip class check)
* \param[in] op Operation action (or NULL to skip op check)
*
* \return true if operation needs meta-data, false otherwise
* \note At least one of rsc_class and op must be specified.
*/
bool
crm_op_needs_metadata(const char *rsc_class, const char *op)
{
/* Agent metadata is used to determine whether an agent reload is possible,
* so if this op is not relevant to that feature, we don't need metadata.
*/
CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
if ((rsc_class != NULL)
&& !pcmk_is_set(pcmk_get_ra_caps(rsc_class), pcmk_ra_cap_params)) {
// Metadata is needed only for resource classes that use parameters
return false;
}
if (op == NULL) {
return true;
}
// Metadata is needed only for these actions
return pcmk__str_any_of(op, PCMK_ACTION_START, PCMK_ACTION_MONITOR,
PCMK_ACTION_PROMOTE, PCMK_ACTION_DEMOTE,
PCMK_ACTION_RELOAD, PCMK_ACTION_RELOAD_AGENT,
PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM,
PCMK_ACTION_NOTIFY, NULL);
}
/*!
* \internal
* \brief Check whether an action name is for a fencing action
*
* \param[in] action Action name to check
*
* \return \c true if \p action is \c PCMK_ACTION_OFF, \c PCMK_ACTION_REBOOT,
* or \c PCMK__ACTION_POWEROFF, otherwise \c false
*/
bool
pcmk__is_fencing_action(const char *action)
{
return pcmk__str_any_of(action, PCMK_ACTION_OFF, PCMK_ACTION_REBOOT,
PCMK__ACTION_POWEROFF, NULL);
}
bool
pcmk_is_probe(const char *task, guint interval)
{
if (task == NULL) {
return false;
}
return (interval == 0)
&& pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none);
}
bool
pcmk_xe_is_probe(const xmlNode *xml_op)
{
const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
const char *interval_ms_s = crm_element_value(xml_op, PCMK_META_INTERVAL);
int interval_ms;
pcmk__scan_min_int(interval_ms_s, &interval_ms, 0);
return pcmk_is_probe(task, interval_ms);
}
bool
pcmk_xe_mask_probe_failure(const xmlNode *xml_op)
{
int status = PCMK_EXEC_UNKNOWN;
int rc = PCMK_OCF_OK;
if (!pcmk_xe_is_probe(xml_op)) {
return false;
}
crm_element_value_int(xml_op, PCMK__XA_OP_STATUS, &status);
crm_element_value_int(xml_op, PCMK__XA_RC_CODE, &rc);
return rc == PCMK_OCF_NOT_INSTALLED || rc == PCMK_OCF_INVALID_PARAM ||
status == PCMK_EXEC_NOT_INSTALLED;
}
diff --git a/lib/common/digest.c b/lib/common/digest.c
index 00e541d84d..ab9a8ef97e 100644
--- a/lib/common/digest.c
+++ b/lib/common/digest.c
@@ -1,278 +1,338 @@
/*
- * Copyright 2015-2023 the Pacemaker project contributors
+ * Copyright 2015-2024 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.
*/
#include <crm_internal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <md5.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include "crmcommon_private.h"
#define BEST_EFFORT_STATUS 0
/*!
* \internal
* \brief Dump XML in a format used with v1 digests
*
* \param[in] xml Root of XML to dump
*
* \return Newly allocated buffer containing dumped XML
*/
static GString *
dump_xml_for_digest(xmlNodePtr xml)
{
GString *buffer = g_string_sized_new(1024);
/* for compatibility with the old result which is used for v1 digests */
g_string_append_c(buffer, ' ');
pcmk__xml2text(xml, 0, buffer, 0);
g_string_append_c(buffer, '\n');
return buffer;
}
/*!
* \brief Calculate and return v1 digest of XML tree
*
* \param[in] input Root of XML to digest
* \param[in] sort Whether to sort the XML before calculating digest
* \param[in] ignored Not used
*
* \return Newly allocated string containing digest
* \note Example return value: "c048eae664dba840e1d2060f00299e9d"
*/
static char *
calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean ignored)
{
char *digest = NULL;
GString *buffer = NULL;
xmlNode *copy = NULL;
if (sort) {
crm_trace("Sorting xml...");
copy = sorted_xml(input, NULL, TRUE);
crm_trace("Done");
input = copy;
}
buffer = dump_xml_for_digest(input);
CRM_CHECK(buffer->len > 0, free_xml(copy);
g_string_free(buffer, TRUE);
return NULL);
digest = crm_md5sum((const char *) buffer->str);
crm_log_xml_trace(input, "digest:source");
g_string_free(buffer, TRUE);
free_xml(copy);
return digest;
}
/*!
* \brief Calculate and return v2 digest of XML tree
*
* \param[in] source Root of XML to digest
* \param[in] do_filter Whether to filter certain XML attributes
*
* \return Newly allocated string containing digest
*/
static char *
calculate_xml_digest_v2(const xmlNode *source, gboolean do_filter)
{
char *digest = NULL;
GString *buffer = g_string_sized_new(1024);
crm_trace("Begin digest %s", do_filter?"filtered":"");
pcmk__xml2text(source, (do_filter? pcmk__xml_fmt_filtered : 0), buffer, 0);
CRM_ASSERT(buffer != NULL);
digest = crm_md5sum((const char *) buffer->str);
pcmk__if_tracing(
{
char *trace_file = crm_strdup_printf("%s/digest-%s",
pcmk__get_tmpdir(), digest);
crm_trace("Saving %s.%s.%s to %s",
crm_element_value(source, PCMK_XA_ADMIN_EPOCH),
crm_element_value(source, PCMK_XA_EPOCH),
crm_element_value(source, PCMK_XA_NUM_UPDATES),
trace_file);
save_xml_to_file(source, "digest input", trace_file);
free(trace_file);
},
{}
);
g_string_free(buffer, TRUE);
crm_trace("End digest");
return digest;
}
/*!
* \brief Calculate and return digest of XML tree, suitable for storing on disk
*
* \param[in] input Root of XML to digest
*
* \return Newly allocated string containing digest
*/
char *
calculate_on_disk_digest(xmlNode *input)
{
/* Always use the v1 format for on-disk digests
* a) it's a compatibility nightmare
* b) we only use this once at startup, all other
* invocations are in a separate child process
*/
return calculate_xml_digest_v1(input, FALSE, FALSE);
}
/*!
* \brief Calculate and return digest of XML operation
*
* \param[in] input Root of XML to digest
* \param[in] version Unused
*
* \return Newly allocated string containing digest
*/
char *
calculate_operation_digest(xmlNode *input, const char *version)
{
/* We still need the sorting for operation digests */
return calculate_xml_digest_v1(input, TRUE, FALSE);
}
/*!
* \brief Calculate and return digest of XML tree
*
* \param[in] input Root of XML to digest
* \param[in] sort Whether to sort XML before calculating digest
* \param[in] do_filter Whether to filter certain XML attributes
* \param[in] version CRM feature set version (used to select v1/v2 digest)
*
* \return Newly allocated string containing digest
*/
char *
calculate_xml_versioned_digest(xmlNode *input, gboolean sort,
gboolean do_filter, const char *version)
{
/*
* @COMPAT digests (on-disk or in diffs/patchsets) created <1.1.4;
* removing this affects even full-restart upgrades from old versions
*
* The sorting associated with v1 digest creation accounted for 23% of
* the CIB manager's CPU usage on the server. v2 drops this.
*
* The filtering accounts for an additional 2.5% and we may want to
* remove it in future.
*
* v2 also uses the xmlBuffer contents directly to avoid additional copying
*/
if (version == NULL || compare_version("3.0.5", version) > 0) {
crm_trace("Using v1 digest algorithm for %s",
pcmk__s(version, "unknown feature set"));
return calculate_xml_digest_v1(input, sort, do_filter);
}
crm_trace("Using v2 digest algorithm for %s",
pcmk__s(version, "unknown feature set"));
return calculate_xml_digest_v2(input, do_filter);
}
/*!
* \internal
* \brief Check whether calculated digest of given XML matches expected digest
*
* \param[in] input Root of XML tree to digest
* \param[in] expected Expected digest in on-disk format
*
* \return true if digests match, false on mismatch or error
*/
bool
pcmk__verify_digest(xmlNode *input, const char *expected)
{
char *calculated = NULL;
bool passed;
if (input != NULL) {
calculated = calculate_on_disk_digest(input);
if (calculated == NULL) {
crm_perror(LOG_ERR, "Could not calculate digest for comparison");
return false;
}
}
passed = pcmk__str_eq(expected, calculated, pcmk__str_casei);
if (passed) {
crm_trace("Digest comparison passed: %s", calculated);
} else {
crm_err("Digest comparison failed: expected %s, calculated %s",
expected, calculated);
}
free(calculated);
return passed;
}
/*!
* \internal
* \brief Check whether an XML attribute should be excluded from CIB digests
*
* \param[in] name XML attribute name
*
* \return true if XML attribute should be excluded from CIB digest calculation
*/
bool
pcmk__xa_filterable(const char *name)
{
static const char *filter[] = {
PCMK_XA_CRM_DEBUG_ORIGIN,
PCMK_XA_CIB_LAST_WRITTEN,
PCMK_XA_UPDATE_ORIGIN,
PCMK_XA_UPDATE_CLIENT,
PCMK_XA_UPDATE_USER,
};
for (int i = 0; i < PCMK__NELEM(filter); i++) {
if (strcmp(name, filter[i]) == 0) {
return true;
}
}
return false;
}
char *
crm_md5sum(const char *buffer)
{
int lpc = 0, len = 0;
char *digest = NULL;
unsigned char raw_digest[MD5_DIGEST_SIZE];
if (buffer == NULL) {
buffer = "";
}
len = strlen(buffer);
crm_trace("Beginning digest of %d bytes", len);
digest = malloc(2 * MD5_DIGEST_SIZE + 1);
if (digest) {
md5_buffer(buffer, len, raw_digest);
for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
}
digest[(2 * MD5_DIGEST_SIZE)] = 0;
crm_trace("Digest %s.", digest);
} else {
crm_err("Could not create digest");
}
return digest;
}
+
+// Return true if a is an attribute that should be filtered
+static bool
+should_filter_for_digest(xmlAttrPtr a, void *user_data)
+{
+ if (strncmp((const char *) a->name, CRM_META "_",
+ sizeof(CRM_META " ") - 1) == 0) {
+ return true;
+ }
+ return pcmk__str_any_of((const char *) a->name,
+ PCMK_XA_ID,
+ PCMK_XA_CRM_FEATURE_SET,
+ PCMK__XA_OP_DIGEST,
+ PCMK__META_ON_NODE,
+ PCMK__META_ON_NODE_UUID,
+ "pcmk_external_ip",
+ NULL);
+}
+
+/*!
+ * \internal
+ * \brief Remove XML attributes not needed for operation digest
+ *
+ * \param[in,out] param_set XML with operation parameters
+ */
+void
+pcmk__filter_op_for_digest(xmlNode *param_set)
+{
+ char *key = NULL;
+ char *timeout = NULL;
+ guint interval_ms = 0;
+
+ if (param_set == NULL) {
+ return;
+ }
+
+ /* Timeout is useful for recurring operation digests, so grab it before
+ * removing meta-attributes
+ */
+ key = crm_meta_name(PCMK_META_INTERVAL);
+ if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
+ interval_ms = 0;
+ }
+ free(key);
+ key = NULL;
+ if (interval_ms != 0) {
+ key = crm_meta_name(PCMK_META_TIMEOUT);
+ timeout = crm_element_value_copy(param_set, key);
+ }
+
+ // Remove all CRM_meta_* attributes and certain other attributes
+ pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
+
+ // Add timeout back for recurring operation digests
+ if (timeout != NULL) {
+ crm_xml_add(param_set, key, timeout);
+ }
+ free(timeout);
+ free(key);
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 6:43 PM (2 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002772
Default Alt Text
(26 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment