pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pe_digest.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <glib.h>
13#include <stdbool.h>
14
15#include <crm/crm.h>
16#include <crm/common/xml.h>
19#include "pe_status_private.h"
20
21extern bool pcmk__is_daemon;
22
32void
33pe__free_digests(gpointer ptr)
34{
36
37 if (data != NULL) {
38 free_xml(data->params_all);
39 free_xml(data->params_secure);
40 free_xml(data->params_restart);
41
42 free(data->digest_all_calc);
43 free(data->digest_restart_calc);
44 free(data->digest_secure_calc);
45
46 free(data);
47 }
48}
49
50// Return true if XML attribute name is not substring of a given string
51static bool
52attr_not_in_string(xmlAttrPtr a, void *user_data)
53{
54 bool filter = false;
55 char *name = crm_strdup_printf(" %s ", (const char *) a->name);
56
57 if (strstr((const char *) user_data, name) == NULL) {
58 crm_trace("Filtering %s (not found in '%s')",
59 (const char *) a->name, (const char *) user_data);
60 filter = true;
61 }
62 free(name);
63 return filter;
64}
65
66// Return true if XML attribute name is substring of a given string
67static bool
68attr_in_string(xmlAttrPtr a, void *user_data)
69{
70 bool filter = false;
71 char *name = crm_strdup_printf(" %s ", (const char *) a->name);
72
73 if (strstr((const char *) user_data, name) != NULL) {
74 crm_trace("Filtering %s (found in '%s')",
75 (const char *) a->name, (const char *) user_data);
76 filter = true;
77 }
78 free(name);
79 return filter;
80}
81
97static void
98calculate_main_digest(pcmk__op_digest_t *data, pcmk_resource_t *rsc,
99 const pcmk_node_t *node, GHashTable *params,
100 const char *task, guint *interval_ms,
101 const xmlNode *xml_op, const char *op_version,
102 GHashTable *overrides, pcmk_scheduler_t *scheduler)
103{
104 xmlNode *action_config = NULL;
105
106 data->params_all = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
107
108 /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers
109 * that themselves are Pacemaker Remote nodes
110 */
111 (void) pe__add_bundle_remote_name(rsc, data->params_all,
113
114 if (overrides != NULL) {
115 // If interval was overridden, reset it
116 const char *meta_name = CRM_META "_" PCMK_META_INTERVAL;
117 const char *interval_s = g_hash_table_lookup(overrides, meta_name);
118
119 if (interval_s != NULL) {
120 long long value_ll;
121
122 if ((pcmk__scan_ll(interval_s, &value_ll, 0LL) == pcmk_rc_ok)
123 && (value_ll >= 0) && (value_ll <= G_MAXUINT)) {
124 *interval_ms = (guint) value_ll;
125 }
126 }
127
128 // Add overrides to list of all parameters
129 g_hash_table_foreach(overrides, hash2field, data->params_all);
130 }
131
132 // Add provided instance parameters
133 g_hash_table_foreach(params, hash2field, data->params_all);
134
135 // Find action configuration XML in CIB
136 action_config = pcmk__find_action_config(rsc, task, *interval_ms, true);
137
138 /* Add action-specific resource instance attributes to the digest list.
139 *
140 * If this is a one-time action with action-specific instance attributes,
141 * enforce a restart instead of reload-agent in case the main digest doesn't
142 * match, even if the restart digest does. This ensures any changes of the
143 * action-specific parameters get applied for this specific action, and
144 * digests calculated for the resulting history will be correct. Default the
145 * result to RSC_DIGEST_RESTART for the case where the main digest doesn't
146 * match.
147 */
148 params = pcmk__unpack_action_rsc_params(action_config, node->details->attrs,
149 scheduler);
150 if ((*interval_ms == 0) && (g_hash_table_size(params) > 0)) {
152 }
153 g_hash_table_foreach(params, hash2field, data->params_all);
154 g_hash_table_destroy(params);
155
156 // Add action meta-attributes
157 params = pcmk__unpack_action_meta(rsc, node, task, *interval_ms,
158 action_config);
159 g_hash_table_foreach(params, hash2metafield, data->params_all);
160 g_hash_table_destroy(params);
161
162 pcmk__filter_op_for_digest(data->params_all);
163
164 data->digest_all_calc = calculate_operation_digest(data->params_all,
165 op_version);
166}
167
168// Return true if XML attribute name is a Pacemaker-defined fencing parameter
169static bool
170is_fence_param(xmlAttrPtr attr, void *user_data)
171{
172 return pcmk_stonith_param((const char *) attr->name);
173}
174
186static void
187calculate_secure_digest(pcmk__op_digest_t *data, const pcmk_resource_t *rsc,
188 GHashTable *params, const xmlNode *xml_op,
189 const char *op_version, GHashTable *overrides)
190{
191 const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
192 const char *secure_list = NULL;
193 bool old_version = (compare_version(op_version, "3.16.0") < 0);
194
195 if (xml_op == NULL) {
196 secure_list = " passwd password user ";
197 } else {
198 secure_list = crm_element_value(xml_op, PCMK__XA_OP_SECURE_PARAMS);
199 }
200
201 if (old_version) {
202 data->params_secure = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
203 if (overrides != NULL) {
204 g_hash_table_foreach(overrides, hash2field, data->params_secure);
205 }
206
207 g_hash_table_foreach(params, hash2field, data->params_secure);
208
209 } else {
210 // Start with a copy of all parameters
211 data->params_secure = pcmk__xml_copy(NULL, data->params_all);
212 }
213
214 if (secure_list != NULL) {
215 pcmk__xe_remove_matching_attrs(data->params_secure, attr_in_string,
216 (void *) secure_list);
217 }
218 if (old_version
221 /* For stonith resources, Pacemaker adds special parameters,
222 * but these are not listed in fence agent meta-data, so with older
223 * versions of DC, the controller will not hash them. That means we have
224 * to filter them out before calculating our hash for comparison.
225 */
226 pcmk__xe_remove_matching_attrs(data->params_secure, is_fence_param,
227 NULL);
228 }
229 pcmk__filter_op_for_digest(data->params_secure);
230
231 /* CRM_meta_timeout *should* be part of a digest for recurring operations.
232 * However, with older versions of DC, the controller does not add timeout
233 * to secure digests, because it only includes parameters declared by the
234 * resource agent.
235 * Remove any timeout that made it this far, to match.
236 */
237 if (old_version) {
238 pcmk__xe_remove_attr(data->params_secure,
240 }
241
242 data->digest_secure_calc = calculate_operation_digest(data->params_secure,
243 op_version);
244}
245
257static void
258calculate_restart_digest(pcmk__op_digest_t *data, const xmlNode *xml_op,
259 const char *op_version)
260{
261 const char *value = NULL;
262
263 // We must have XML of resource operation history
264 if (xml_op == NULL) {
265 return;
266 }
267
268 // And the history must have a restart digest to compare against
269 if (crm_element_value(xml_op, PCMK__XA_OP_RESTART_DIGEST) == NULL) {
270 return;
271 }
272
273 // Start with a copy of all parameters
274 data->params_restart = pcmk__xml_copy(NULL, data->params_all);
275
276 // Then filter out reloadable parameters, if any
278 if (value != NULL) {
279 pcmk__xe_remove_matching_attrs(data->params_restart, attr_not_in_string,
280 (void *) value);
281 }
282
284 data->digest_restart_calc = calculate_operation_digest(data->params_restart,
285 value);
286}
287
307 guint *interval_ms, const pcmk_node_t *node,
308 const xmlNode *xml_op, GHashTable *overrides,
309 bool calc_secure, pcmk_scheduler_t *scheduler)
310{
311 pcmk__op_digest_t *data = NULL;
312 const char *op_version = NULL;
313 GHashTable *params = NULL;
314
315 CRM_CHECK(scheduler != NULL, return NULL);
316
317 data = calloc(1, sizeof(pcmk__op_digest_t));
318 if (data == NULL) {
319 pcmk__sched_err("Could not allocate memory for operation digest");
320 return NULL;
321 }
322
324
325 if (xml_op != NULL) {
326 op_version = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET);
327 }
328
329 if ((op_version == NULL) && (scheduler->input != NULL)) {
330 op_version = crm_element_value(scheduler->input,
332 }
333
334 if (op_version == NULL) {
335 op_version = CRM_FEATURE_SET;
336 }
337
338 params = pe_rsc_params(rsc, node, scheduler);
339 calculate_main_digest(data, rsc, node, params, task, interval_ms, xml_op,
340 op_version, overrides, scheduler);
341 if (calc_secure) {
342 calculate_secure_digest(data, rsc, params, xml_op, op_version,
343 overrides);
344 }
345 calculate_restart_digest(data, xml_op, op_version);
346 return data;
347}
348
363static pcmk__op_digest_t *
364rsc_action_digest(pcmk_resource_t *rsc, const char *task, guint interval_ms,
365 pcmk_node_t *node, const xmlNode *xml_op,
366 bool calc_secure, pcmk_scheduler_t *scheduler)
367{
368 pcmk__op_digest_t *data = NULL;
369 char *key = pcmk__op_key(rsc->id, task, interval_ms);
370
371 data = g_hash_table_lookup(node->details->digest_cache, key);
372 if (data == NULL) {
373 data = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
374 NULL, calc_secure, scheduler);
375 CRM_ASSERT(data != NULL);
376 g_hash_table_insert(node->details->digest_cache, strdup(key), data);
377 }
378 free(key);
379 return data;
380}
381
394rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op,
396{
397 pcmk__op_digest_t *data = NULL;
398 guint interval_ms = 0;
399
400 const char *op_version;
401 const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
402 const char *digest_all;
403 const char *digest_restart;
404
405 CRM_ASSERT(node != NULL);
406
407 op_version = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET);
408 digest_all = crm_element_value(xml_op, PCMK__XA_OP_DIGEST);
409 digest_restart = crm_element_value(xml_op, PCMK__XA_OP_RESTART_DIGEST);
410
411 crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
412 data = rsc_action_digest(rsc, task, interval_ms, node, xml_op,
415 scheduler);
416
417 if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
418 pcmk__rsc_info(rsc,
419 "Parameters to %ums-interval %s action for %s on %s "
420 "changed: hash was %s vs. now %s (restart:%s) %s",
421 interval_ms, task, rsc->id, pcmk__node_name(node),
422 pcmk__s(digest_restart, "missing"),
423 data->digest_restart_calc, op_version,
426
427 } else if (digest_all == NULL) {
428 /* it is unknown what the previous op digest was */
430
431 } else if (strcmp(digest_all, data->digest_all_calc) != 0) {
432 /* Given a non-recurring operation with extra parameters configured,
433 * in case that the main digest doesn't match, even if the restart
434 * digest matches, enforce a restart rather than a reload-agent anyway.
435 * So that it ensures any changes of the extra parameters get applied
436 * for this specific operation, and the digests calculated for the
437 * resulting PCMK__XE_LRM_RSC_OP will be correct.
438 * Preserve the implied rc pcmk__digest_restart for the case that the
439 * main digest doesn't match.
440 */
441 if ((interval_ms == 0) && (data->rc == pcmk__digest_restart)) {
442 pcmk__rsc_info(rsc,
443 "Parameters containing extra ones to %ums-interval"
444 " %s action for %s on %s "
445 "changed: hash was %s vs. now %s (restart:%s) %s",
446 interval_ms, task, rsc->id, pcmk__node_name(node),
447 pcmk__s(digest_all, "missing"),
448 data->digest_all_calc, op_version,
449 crm_element_value(xml_op,
451
452 } else {
453 pcmk__rsc_info(rsc,
454 "Parameters to %ums-interval %s action for %s on %s "
455 "changed: hash was %s vs. now %s (%s:%s) %s",
456 interval_ms, task, rsc->id, pcmk__node_name(node),
457 pcmk__s(digest_all, "missing"),
458 data->digest_all_calc,
459 (interval_ms > 0)? "reschedule" : "reload",
460 op_version,
461 crm_element_value(xml_op,
464 }
465
466 } else {
468 }
469 return data;
470}
471
489static inline char *
490create_unfencing_summary(const char *rsc_id, const char *agent_type,
491 const char *param_digest)
492{
493 return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest);
494}
495
512static bool
513unfencing_digest_matches(const char *rsc_id, const char *agent,
514 const char *digest_calc, const char *node_summary)
515{
516 bool matches = FALSE;
517
518 if (rsc_id && agent && digest_calc && node_summary) {
519 char *search_secure = create_unfencing_summary(rsc_id, agent,
520 digest_calc);
521
522 /* The digest was calculated including the device ID and agent,
523 * so there is no risk of collision using strstr().
524 */
525 matches = (strstr(node_summary, search_secure) != NULL);
526 crm_trace("Calculated unfencing digest '%s' %sfound in '%s'",
527 search_secure, matches? "" : "not ", node_summary);
528 free(search_secure);
529 }
530 return matches;
531}
532
533/* Magic string to use as action name for digest cache entries used for
534 * unfencing checks. This is not a real action name (i.e. "on"), so
535 * pcmk__check_action_config() won't confuse these entries with real actions.
536 */
537#define STONITH_DIGEST_TASK "stonith-on"
538
553{
554 const char *node_summary = NULL;
555
556 // Calculate device's current parameter digests
557 pcmk__op_digest_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, 0U,
558 node, NULL, TRUE, scheduler);
559
560 // Check whether node has special unfencing summary node attribute
561 node_summary = pcmk__node_attr(node, CRM_ATTR_DIGESTS_ALL, NULL,
563 if (node_summary == NULL) {
565 return data;
566 }
567
568 // Check whether full parameter digest matches
569 if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc,
570 node_summary)) {
572 return data;
573 }
574
575 // Check whether secure parameter digest matches
576 node_summary = pcmk__node_attr(node, CRM_ATTR_DIGESTS_SECURE, NULL,
578 if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
579 node_summary)) {
581 if (!pcmk__is_daemon && scheduler->priv != NULL) {
583 out->info(out, "Only 'private' parameters to %s "
584 "for unfencing %s changed", rsc->id,
585 pcmk__node_name(node));
586 }
587 return data;
588 }
589
590 // Parameters don't match
593 && (data->digest_secure_calc != NULL)) {
594
595 if (scheduler->priv != NULL) {
597 char *digest = create_unfencing_summary(rsc->id, agent,
598 data->digest_secure_calc);
599
600 out->info(out, "Parameters to %s for unfencing "
601 "%s changed, try '%s'", rsc->id,
602 pcmk__node_name(node), digest);
603 free(digest);
604 } else if (!pcmk__is_daemon) {
605 char *digest = create_unfencing_summary(rsc->id, agent,
606 data->digest_secure_calc);
607
608 printf("Parameters to %s for unfencing %s changed, try '%s'\n",
609 rsc->id, pcmk__node_name(node), digest);
610 free(digest);
611 }
612 }
613 return data;
614}
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition digest.c:303
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition actions.c:196
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition agents.c:31
bool pcmk_stonith_param(const char *param)
Check whether a given stonith parameter is handled by Pacemaker.
Definition agents.c:175
@ pcmk_ra_cap_fence_params
Definition agents.h:65
const char * pcmk__node_attr(const pcmk_node_t *node, const char *name, const char *target, enum pcmk__rsc_node node_type)
Definition attrs.c:118
const char * name
Definition cib.c:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int compare_version(const char *version1, const char *version2)
Definition utils.c:188
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
GHashTable * pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Get a table of resource parameters.
Definition complex.c:484
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_ATTR_DIGESTS_ALL
Definition crm.h:107
#define CRM_FEATURE_SET
Definition crm.h:72
#define CRM_META
Definition crm.h:81
#define CRM_ATTR_DIGESTS_SECURE
Definition crm.h:108
@ pcmk__digest_match
@ pcmk__digest_restart
@ pcmk__digest_mismatch
@ pcmk__digest_unknown
@ pcmk__rsc_node_current
Where resource is running.
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_trace(fmt, args...)
Definition logging.h:402
pcmk_scheduler_t * scheduler
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition nvpair.c:764
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:736
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition nvpair.c:539
#define PCMK_META_INTERVAL
Definition options.h:91
#define PCMK_REMOTE_RA_ADDR
Definition options.h:122
#define PCMK_META_TIMEOUT
Definition options.h:114
pcmk__op_digest_t * rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:394
#define STONITH_DIGEST_TASK
Definition pe_digest.c:537
pcmk__op_digest_t * pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:551
pcmk__op_digest_t * pe__calculate_digests(pcmk_resource_t *rsc, const char *task, guint *interval_ms, const pcmk_node_t *node, const xmlNode *xml_op, GHashTable *overrides, bool calc_secure, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:306
void pe__free_digests(gpointer ptr)
Definition pe_digest.c:33
bool pcmk__is_daemon
Definition logging.c:47
GHashTable * pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, const char *action_name, guint interval_ms, const xmlNode *action_config)
Definition pe_actions.c:702
const char * pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml, const char *field)
Definition bundle.c:938
GHashTable * pcmk__unpack_action_rsc_params(const xmlNode *action_xml, GHashTable *node_attrs, pcmk_scheduler_t *data_set)
Definition pe_actions.c:241
xmlNode * pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled)
Definition pe_actions.c:132
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_sched_sanitized
Definition scheduler.h:161
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__sched_err(fmt...)
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:97
This structure contains everything that makes up a single output formatter.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
struct pe_node_shared_s * details
Definition nodes.h:167
GHashTable * attrs
Definition nodes.h:142
GHashTable * digest_cache
Definition nodes.h:144
xmlNode * input
Definition scheduler.h:196
unsigned long long flags
Definition scheduler.h:211
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition xml.c:867
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition digest.c:148
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition xml.c:652
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition xml.c:692
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition xml.c:720
#define PCMK_XA_CLASS
Definition xml_names.h:241
#define PCMK_XA_OPERATION
Definition xml_names.h:344
#define PCMK_XA_CRM_FEATURE_SET
Definition xml_names.h:249
#define PCMK_XE_PARAMETERS
Definition xml_names.h:156
#define PCMK__XA_OP_DIGEST
#define PCMK__XA_TRANSITION_MAGIC
#define PCMK__XA_OP_RESTART_DIGEST
#define PCMK__XA_OP_FORCE_RESTART
#define PCMK__XA_OP_SECURE_PARAMS