pacemaker 2.1.7-2.1.7
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pe_digest.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2023 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/msg_xml.h>
17#include <crm/common/xml.h>
20#include "pe_status_private.h"
21
22extern bool pcmk__is_daemon;
23
33void
34pe__free_digests(gpointer ptr)
35{
37
38 if (data != NULL) {
39 free_xml(data->params_all);
40 free_xml(data->params_secure);
41 free_xml(data->params_restart);
42
43 free(data->digest_all_calc);
44 free(data->digest_restart_calc);
45 free(data->digest_secure_calc);
46
47 free(data);
48 }
49}
50
51// Return true if XML attribute name is not substring of a given string
52static bool
53attr_not_in_string(xmlAttrPtr a, void *user_data)
54{
55 bool filter = false;
56 char *name = crm_strdup_printf(" %s ", (const char *) a->name);
57
58 if (strstr((const char *) user_data, name) == NULL) {
59 crm_trace("Filtering %s (not found in '%s')",
60 (const char *) a->name, (const char *) user_data);
61 filter = true;
62 }
63 free(name);
64 return filter;
65}
66
67// Return true if XML attribute name is substring of a given string
68static bool
69attr_in_string(xmlAttrPtr a, void *user_data)
70{
71 bool filter = false;
72 char *name = crm_strdup_printf(" %s ", (const char *) a->name);
73
74 if (strstr((const char *) user_data, name) != NULL) {
75 crm_trace("Filtering %s (found in '%s')",
76 (const char *) a->name, (const char *) user_data);
77 filter = true;
78 }
79 free(name);
80 return filter;
81}
82
98static void
99calculate_main_digest(op_digest_cache_t *data, pcmk_resource_t *rsc,
100 const pcmk_node_t *node, GHashTable *params,
101 const char *task, guint *interval_ms,
102 const xmlNode *xml_op, const char *op_version,
103 GHashTable *overrides, pcmk_scheduler_t *scheduler)
104{
105 xmlNode *action_config = NULL;
106
107 data->params_all = create_xml_node(NULL, XML_TAG_PARAMS);
108
109 /* REMOTE_CONTAINER_HACK: Allow Pacemaker Remote nodes to run containers
110 * that themselves are Pacemaker Remote nodes
111 */
112 (void) pe__add_bundle_remote_name(rsc, scheduler, data->params_all,
114
115 if (overrides != NULL) {
116 // If interval was overridden, reset it
117 const char *interval_s = g_hash_table_lookup(overrides, CRM_META "_"
119
120 if (interval_s != NULL) {
121 long long value_ll;
122
123 if ((pcmk__scan_ll(interval_s, &value_ll, 0LL) == pcmk_rc_ok)
124 && (value_ll >= 0) && (value_ll <= G_MAXUINT)) {
125 *interval_ms = (guint) value_ll;
126 }
127 }
128
129 // Add overrides to list of all parameters
130 g_hash_table_foreach(overrides, hash2field, data->params_all);
131 }
132
133 // Add provided instance parameters
134 g_hash_table_foreach(params, hash2field, data->params_all);
135
136 // Find action configuration XML in CIB
137 action_config = pcmk__find_action_config(rsc, task, *interval_ms, true);
138
139 /* Add action-specific resource instance attributes to the digest list.
140 *
141 * If this is a one-time action with action-specific instance attributes,
142 * enforce a restart instead of reload-agent in case the main digest doesn't
143 * match, even if the restart digest does. This ensures any changes of the
144 * action-specific parameters get applied for this specific action, and
145 * digests calculated for the resulting history will be correct. Default the
146 * result to RSC_DIGEST_RESTART for the case where the main digest doesn't
147 * match.
148 */
149 params = pcmk__unpack_action_rsc_params(action_config, node->details->attrs,
150 scheduler);
151 if ((*interval_ms == 0) && (g_hash_table_size(params) > 0)) {
153 }
154 g_hash_table_foreach(params, hash2field, data->params_all);
155 g_hash_table_destroy(params);
156
157 // Add action meta-attributes
158 params = pcmk__unpack_action_meta(rsc, node, task, *interval_ms,
159 action_config);
160 g_hash_table_foreach(params, hash2metafield, data->params_all);
161 g_hash_table_destroy(params);
162
163 pcmk__filter_op_for_digest(data->params_all);
164
165 data->digest_all_calc = calculate_operation_digest(data->params_all,
166 op_version);
167}
168
169// Return true if XML attribute name is a Pacemaker-defined fencing parameter
170static bool
171is_fence_param(xmlAttrPtr attr, void *user_data)
172{
173 return pcmk_stonith_param((const char *) attr->name);
174}
175
187static void
188calculate_secure_digest(op_digest_cache_t *data, const pcmk_resource_t *rsc,
189 GHashTable *params, const xmlNode *xml_op,
190 const char *op_version, GHashTable *overrides)
191{
192 const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
193 const char *secure_list = NULL;
194 bool old_version = (compare_version(op_version, "3.16.0") < 0);
195
196 if (xml_op == NULL) {
197 secure_list = " passwd password user ";
198 } else {
199 secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE);
200 }
201
202 if (old_version) {
203 data->params_secure = create_xml_node(NULL, XML_TAG_PARAMS);
204 if (overrides != NULL) {
205 g_hash_table_foreach(overrides, hash2field, data->params_secure);
206 }
207
208 g_hash_table_foreach(params, hash2field, data->params_secure);
209
210 } else {
211 // Start with a copy of all parameters
212 data->params_secure = copy_xml(data->params_all);
213 }
214
215 if (secure_list != NULL) {
216 pcmk__xe_remove_matching_attrs(data->params_secure, attr_in_string,
217 (void *) secure_list);
218 }
219 if (old_version
222 /* For stonith resources, Pacemaker adds special parameters,
223 * but these are not listed in fence agent meta-data, so with older
224 * versions of DC, the controller will not hash them. That means we have
225 * to filter them out before calculating our hash for comparison.
226 */
227 pcmk__xe_remove_matching_attrs(data->params_secure, is_fence_param,
228 NULL);
229 }
230 pcmk__filter_op_for_digest(data->params_secure);
231
232 /* CRM_meta_timeout *should* be part of a digest for recurring operations.
233 * However, with older versions of DC, the controller does not add timeout
234 * to secure digests, because it only includes parameters declared by the
235 * resource agent.
236 * Remove any timeout that made it this far, to match.
237 */
238 if (old_version) {
239 xml_remove_prop(data->params_secure, CRM_META "_" XML_ATTR_TIMEOUT);
240 }
241
242 data->digest_secure_calc = calculate_operation_digest(data->params_secure,
243 op_version);
244}
245
257static void
258calculate_restart_digest(op_digest_cache_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, XML_LRM_ATTR_RESTART_DIGEST) == NULL) {
270 return;
271 }
272
273 // Start with a copy of all parameters
274 data->params_restart = copy_xml(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 op_digest_cache_t *data = calloc(1, sizeof(op_digest_cache_t));
312 const char *op_version = NULL;
313 GHashTable *params = NULL;
314
315 if (data == NULL) {
316 return NULL;
317 }
318
320
321 if (xml_op != NULL) {
322 op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
323 }
324
325 if (op_version == NULL && scheduler != NULL && scheduler->input != NULL) {
327 }
328
329 if (op_version == NULL) {
330 op_version = CRM_FEATURE_SET;
331 }
332
333 params = pe_rsc_params(rsc, node, scheduler);
334 calculate_main_digest(data, rsc, node, params, task, interval_ms, xml_op,
335 op_version, overrides, scheduler);
336 if (calc_secure) {
337 calculate_secure_digest(data, rsc, params, xml_op, op_version,
338 overrides);
339 }
340 calculate_restart_digest(data, xml_op, op_version);
341 return data;
342}
343
358static op_digest_cache_t *
359rsc_action_digest(pcmk_resource_t *rsc, const char *task, guint interval_ms,
360 pcmk_node_t *node, const xmlNode *xml_op,
361 bool calc_secure, pcmk_scheduler_t *scheduler)
362{
363 op_digest_cache_t *data = NULL;
364 char *key = pcmk__op_key(rsc->id, task, interval_ms);
365
366 data = g_hash_table_lookup(node->details->digest_cache, key);
367 if (data == NULL) {
368 data = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
369 NULL, calc_secure, scheduler);
370 CRM_ASSERT(data != NULL);
371 g_hash_table_insert(node->details->digest_cache, strdup(key), data);
372 }
373 free(key);
374 return data;
375}
376
389rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op,
391{
392 op_digest_cache_t *data = NULL;
393 guint interval_ms = 0;
394
395 const char *op_version;
396 const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
397 const char *digest_all;
398 const char *digest_restart;
399
400 CRM_ASSERT(node != NULL);
401
402 op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
403 digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
404 digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
405
406 crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
407 data = rsc_action_digest(rsc, task, interval_ms, node, xml_op,
410 scheduler);
411
412 if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
413 pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s "
414 "changed: hash was %s vs. now %s (restart:%s) %s",
415 interval_ms, task, rsc->id, pe__node_name(node),
416 pcmk__s(digest_restart, "missing"),
417 data->digest_restart_calc,
418 op_version,
421
422 } else if (digest_all == NULL) {
423 /* it is unknown what the previous op digest was */
425
426 } else if (strcmp(digest_all, data->digest_all_calc) != 0) {
427 /* Given a non-recurring operation with extra parameters configured,
428 * in case that the main digest doesn't match, even if the restart
429 * digest matches, enforce a restart rather than a reload-agent anyway.
430 * So that it ensures any changes of the extra parameters get applied
431 * for this specific operation, and the digests calculated for the
432 * resulting lrm_rsc_op will be correct.
433 * Preserve the implied rc pcmk__digest_restart for the case that the
434 * main digest doesn't match.
435 */
436 if ((interval_ms == 0) && (data->rc == pcmk__digest_restart)) {
437 pe_rsc_info(rsc, "Parameters containing extra ones to %ums-interval"
438 " %s action for %s on %s "
439 "changed: hash was %s vs. now %s (restart:%s) %s",
440 interval_ms, task, rsc->id, pe__node_name(node),
441 pcmk__s(digest_all, "missing"), data->digest_all_calc,
442 op_version,
444
445 } else {
446 pe_rsc_info(rsc, "Parameters to %ums-interval %s action for %s on %s "
447 "changed: hash was %s vs. now %s (%s:%s) %s",
448 interval_ms, task, rsc->id, pe__node_name(node),
449 pcmk__s(digest_all, "missing"), data->digest_all_calc,
450 (interval_ms > 0)? "reschedule" : "reload",
451 op_version,
454 }
455
456 } else {
458 }
459 return data;
460}
461
479static inline char *
480create_unfencing_summary(const char *rsc_id, const char *agent_type,
481 const char *param_digest)
482{
483 return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest);
484}
485
502static bool
503unfencing_digest_matches(const char *rsc_id, const char *agent,
504 const char *digest_calc, const char *node_summary)
505{
506 bool matches = FALSE;
507
508 if (rsc_id && agent && digest_calc && node_summary) {
509 char *search_secure = create_unfencing_summary(rsc_id, agent,
510 digest_calc);
511
512 /* The digest was calculated including the device ID and agent,
513 * so there is no risk of collision using strstr().
514 */
515 matches = (strstr(node_summary, search_secure) != NULL);
516 crm_trace("Calculated unfencing digest '%s' %sfound in '%s'",
517 search_secure, matches? "" : "not ", node_summary);
518 free(search_secure);
519 }
520 return matches;
521}
522
523/* Magic string to use as action name for digest cache entries used for
524 * unfencing checks. This is not a real action name (i.e. "on"), so
525 * pcmk__check_action_config() won't confuse these entries with real actions.
526 */
527#define STONITH_DIGEST_TASK "stonith-on"
528
543{
544 const char *node_summary = NULL;
545
546 // Calculate device's current parameter digests
547 op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, 0U,
548 node, NULL, TRUE, scheduler);
549
550 // Check whether node has special unfencing summary node attribute
551 node_summary = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
552 if (node_summary == NULL) {
554 return data;
555 }
556
557 // Check whether full parameter digest matches
558 if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc,
559 node_summary)) {
561 return data;
562 }
563
564 // Check whether secure parameter digest matches
566 if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
567 node_summary)) {
569 if (!pcmk__is_daemon && scheduler->priv != NULL) {
571 out->info(out, "Only 'private' parameters to %s "
572 "for unfencing %s changed", rsc->id,
573 pe__node_name(node));
574 }
575 return data;
576 }
577
578 // Parameters don't match
581 && (data->digest_secure_calc != NULL)) {
582
583 if (scheduler->priv != NULL) {
585 char *digest = create_unfencing_summary(rsc->id, agent,
586 data->digest_secure_calc);
587
588 out->info(out, "Parameters to %s for unfencing "
589 "%s changed, try '%s'", rsc->id,
590 pe__node_name(node), digest);
591 free(digest);
592 } else if (!pcmk__is_daemon) {
593 char *digest = create_unfencing_summary(rsc->id, agent,
594 data->digest_secure_calc);
595
596 printf("Parameters to %s for unfencing %s changed, try '%s'\n",
597 rsc->id, pe__node_name(node), digest);
598 free(digest);
599 }
600 }
601 return data;
602}
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition actions.c:344
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:42
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:174
@ pcmk_ra_cap_fence_params
Definition agents.h:65
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:189
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:99
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:446
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_ATTR_DIGESTS_ALL
Definition crm.h:121
#define CRM_FEATURE_SET
Definition crm.h:70
#define CRM_META
Definition crm.h:79
#define CRM_ATTR_DIGESTS_SECURE
Definition crm.h:122
@ pcmk__digest_match
@ pcmk__digest_restart
@ pcmk__digest_mismatch
@ pcmk__digest_unknown
#define crm_trace(fmt, args...)
Definition logging.h:385
#define XML_ATTR_CRM_VERSION
Definition msg_xml.h:140
#define XML_LRM_ATTR_OP_DIGEST
Definition msg_xml.h:319
#define XML_LRM_ATTR_OP_SECURE
Definition msg_xml.h:321
#define XML_LRM_ATTR_INTERVAL
Definition msg_xml.h:300
#define XML_LRM_ATTR_OP_RESTART
Definition msg_xml.h:320
#define XML_ATTR_TRANSITION_MAGIC
Definition msg_xml.h:415
#define XML_LRM_ATTR_RESTART_DIGEST
Definition msg_xml.h:322
#define XML_AGENT_ATTR_CLASS
Definition msg_xml.h:280
#define XML_LRM_ATTR_TASK
Definition msg_xml.h:306
#define XML_ATTR_TIMEOUT
Definition msg_xml.h:150
#define XML_RSC_ATTR_REMOTE_RA_ADDR
Definition msg_xml.h:261
#define XML_TAG_PARAMS
Definition msg_xml.h:230
#define XML_LRM_ATTR_INTERVAL_MS
Definition msg_xml.h:304
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:447
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition nvpair.c:728
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:700
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition nvpair.c:540
op_digest_cache_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
#define STONITH_DIGEST_TASK
Definition pe_digest.c:527
op_digest_cache_t * pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:541
op_digest_cache_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:389
void pe__free_digests(gpointer ptr)
Definition pe_digest.c:34
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:692
GHashTable * pcmk__unpack_action_rsc_params(const xmlNode *action_xml, GHashTable *node_attrs, pcmk_scheduler_t *data_set)
Definition pe_actions.c:240
#define pe_rsc_info(rsc, fmt, args...)
Definition internal.h:35
const char * pe__add_bundle_remote_name(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler, xmlNode *xml, const char *field)
Definition bundle.c:938
xmlNode * pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled)
Definition pe_actions.c:129
const char * pe_node_attribute_raw(const pcmk_node_t *node, const char *name)
Definition common.c:621
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:154
@ pcmk_sched_sanitized
Whether sensitive resource attributes have been masked.
Definition scheduler.h:146
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
Implementation of pcmk_node_t.
Definition nodes.h:130
struct pe_node_shared_s * details
Basic node information.
Definition nodes.h:134
GHashTable * attrs
Node attributes.
Definition nodes.h:115
GHashTable * digest_cache
Cache of calculated resource digests.
Definition nodes.h:117
Implementation of pcmk_resource_t.
Definition resources.h:399
char * id
Resource ID in configuration.
Definition resources.h:400
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
xmlNode * input
CIB XML.
Definition scheduler.h:175
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition scheduler.h:183
void * priv
For Pacemaker use only.
Definition scheduler.h:229
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition xml.c:783
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:789
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:638
void xml_remove_prop(xmlNode *obj, const char *name)
Definition xml.c:1696
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition digest.c:150
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition xml.c:593