Line data Source code
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>
17 : #include <crm/common/xml_internal.h>
18 : #include <crm/pengine/internal.h>
19 : #include "pe_status_private.h"
20 :
21 : extern bool pcmk__is_daemon;
22 :
23 : /*!
24 : * \internal
25 : * \brief Free an operation digest cache entry
26 : *
27 : * \param[in,out] ptr Pointer to cache entry to free
28 : *
29 : * \note The argument is a gpointer so this can be used as a hash table
30 : * free function.
31 : */
32 : void
33 0 : pe__free_digests(gpointer ptr)
34 : {
35 0 : pcmk__op_digest_t *data = ptr;
36 :
37 0 : if (data != NULL) {
38 0 : free_xml(data->params_all);
39 0 : free_xml(data->params_secure);
40 0 : free_xml(data->params_restart);
41 :
42 0 : free(data->digest_all_calc);
43 0 : free(data->digest_restart_calc);
44 0 : free(data->digest_secure_calc);
45 :
46 0 : free(data);
47 : }
48 0 : }
49 :
50 : // Return true if XML attribute name is not substring of a given string
51 : static bool
52 0 : attr_not_in_string(xmlAttrPtr a, void *user_data)
53 : {
54 0 : bool filter = false;
55 0 : char *name = crm_strdup_printf(" %s ", (const char *) a->name);
56 :
57 0 : if (strstr((const char *) user_data, name) == NULL) {
58 0 : crm_trace("Filtering %s (not found in '%s')",
59 : (const char *) a->name, (const char *) user_data);
60 0 : filter = true;
61 : }
62 0 : free(name);
63 0 : return filter;
64 : }
65 :
66 : // Return true if XML attribute name is substring of a given string
67 : static bool
68 0 : attr_in_string(xmlAttrPtr a, void *user_data)
69 : {
70 0 : bool filter = false;
71 0 : char *name = crm_strdup_printf(" %s ", (const char *) a->name);
72 :
73 0 : if (strstr((const char *) user_data, name) != NULL) {
74 0 : crm_trace("Filtering %s (found in '%s')",
75 : (const char *) a->name, (const char *) user_data);
76 0 : filter = true;
77 : }
78 0 : free(name);
79 0 : return filter;
80 : }
81 :
82 : /*!
83 : * \internal
84 : * \brief Add digest of all parameters to a digest cache entry
85 : *
86 : * \param[out] data Digest cache entry to modify
87 : * \param[in,out] rsc Resource that action was for
88 : * \param[in] node Node action was performed on
89 : * \param[in] params Resource parameters evaluated for node
90 : * \param[in] task Name of action performed
91 : * \param[in,out] interval_ms Action's interval (will be reset if in overrides)
92 : * \param[in] xml_op Unused
93 : * \param[in] op_version CRM feature set to use for digest calculation
94 : * \param[in] overrides Key/value table to override resource parameters
95 : * \param[in,out] scheduler Scheduler data
96 : */
97 : static void
98 0 : calculate_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 0 : xmlNode *action_config = NULL;
105 :
106 0 : 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 0 : (void) pe__add_bundle_remote_name(rsc, data->params_all,
112 : PCMK_REMOTE_RA_ADDR);
113 :
114 0 : if (overrides != NULL) {
115 : // If interval was overridden, reset it
116 0 : const char *meta_name = CRM_META "_" PCMK_META_INTERVAL;
117 0 : const char *interval_s = g_hash_table_lookup(overrides, meta_name);
118 :
119 0 : if (interval_s != NULL) {
120 : long long value_ll;
121 :
122 0 : if ((pcmk__scan_ll(interval_s, &value_ll, 0LL) == pcmk_rc_ok)
123 0 : && (value_ll >= 0) && (value_ll <= G_MAXUINT)) {
124 0 : *interval_ms = (guint) value_ll;
125 : }
126 : }
127 :
128 : // Add overrides to list of all parameters
129 0 : g_hash_table_foreach(overrides, hash2field, data->params_all);
130 : }
131 :
132 : // Add provided instance parameters
133 0 : g_hash_table_foreach(params, hash2field, data->params_all);
134 :
135 : // Find action configuration XML in CIB
136 0 : 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 0 : params = pcmk__unpack_action_rsc_params(action_config, node->details->attrs,
149 : scheduler);
150 0 : if ((*interval_ms == 0) && (g_hash_table_size(params) > 0)) {
151 0 : data->rc = pcmk__digest_restart;
152 : }
153 0 : g_hash_table_foreach(params, hash2field, data->params_all);
154 0 : g_hash_table_destroy(params);
155 :
156 : // Add action meta-attributes
157 0 : params = pcmk__unpack_action_meta(rsc, node, task, *interval_ms,
158 : action_config);
159 0 : g_hash_table_foreach(params, hash2metafield, data->params_all);
160 0 : g_hash_table_destroy(params);
161 :
162 0 : pcmk__filter_op_for_digest(data->params_all);
163 :
164 0 : data->digest_all_calc = calculate_operation_digest(data->params_all,
165 : op_version);
166 0 : }
167 :
168 : // Return true if XML attribute name is a Pacemaker-defined fencing parameter
169 : static bool
170 0 : is_fence_param(xmlAttrPtr attr, void *user_data)
171 : {
172 0 : return pcmk_stonith_param((const char *) attr->name);
173 : }
174 :
175 : /*!
176 : * \internal
177 : * \brief Add secure digest to a digest cache entry
178 : *
179 : * \param[out] data Digest cache entry to modify
180 : * \param[in] rsc Resource that action was for
181 : * \param[in] params Resource parameters evaluated for node
182 : * \param[in] xml_op XML of operation in CIB status (if available)
183 : * \param[in] op_version CRM feature set to use for digest calculation
184 : * \param[in] overrides Key/value hash table to override resource parameters
185 : */
186 : static void
187 0 : calculate_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 0 : const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
192 0 : const char *secure_list = NULL;
193 0 : bool old_version = (compare_version(op_version, "3.16.0") < 0);
194 :
195 0 : if (xml_op == NULL) {
196 0 : secure_list = " passwd password user ";
197 : } else {
198 0 : secure_list = crm_element_value(xml_op, PCMK__XA_OP_SECURE_PARAMS);
199 : }
200 :
201 0 : if (old_version) {
202 0 : data->params_secure = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
203 0 : if (overrides != NULL) {
204 0 : g_hash_table_foreach(overrides, hash2field, data->params_secure);
205 : }
206 :
207 0 : g_hash_table_foreach(params, hash2field, data->params_secure);
208 :
209 : } else {
210 : // Start with a copy of all parameters
211 0 : data->params_secure = pcmk__xml_copy(NULL, data->params_all);
212 : }
213 :
214 0 : if (secure_list != NULL) {
215 0 : pcmk__xe_remove_matching_attrs(data->params_secure, attr_in_string,
216 : (void *) secure_list);
217 : }
218 0 : if (old_version
219 0 : && pcmk_is_set(pcmk_get_ra_caps(class),
220 : pcmk_ra_cap_fence_params)) {
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 0 : pcmk__xe_remove_matching_attrs(data->params_secure, is_fence_param,
227 : NULL);
228 : }
229 0 : 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 0 : if (old_version) {
238 0 : pcmk__xe_remove_attr(data->params_secure,
239 : CRM_META "_" PCMK_META_TIMEOUT);
240 : }
241 :
242 0 : data->digest_secure_calc = calculate_operation_digest(data->params_secure,
243 : op_version);
244 0 : }
245 :
246 : /*!
247 : * \internal
248 : * \brief Add restart digest to a digest cache entry
249 : *
250 : * \param[out] data Digest cache entry to modify
251 : * \param[in] xml_op XML of operation in CIB status (if available)
252 : * \param[in] op_version CRM feature set to use for digest calculation
253 : *
254 : * \note This function doesn't need to handle overrides because it starts with
255 : * data->params_all, which already has overrides applied.
256 : */
257 : static void
258 0 : calculate_restart_digest(pcmk__op_digest_t *data, const xmlNode *xml_op,
259 : const char *op_version)
260 : {
261 0 : const char *value = NULL;
262 :
263 : // We must have XML of resource operation history
264 0 : if (xml_op == NULL) {
265 0 : return;
266 : }
267 :
268 : // And the history must have a restart digest to compare against
269 0 : if (crm_element_value(xml_op, PCMK__XA_OP_RESTART_DIGEST) == NULL) {
270 0 : return;
271 : }
272 :
273 : // Start with a copy of all parameters
274 0 : data->params_restart = pcmk__xml_copy(NULL, data->params_all);
275 :
276 : // Then filter out reloadable parameters, if any
277 0 : value = crm_element_value(xml_op, PCMK__XA_OP_FORCE_RESTART);
278 0 : if (value != NULL) {
279 0 : pcmk__xe_remove_matching_attrs(data->params_restart, attr_not_in_string,
280 : (void *) value);
281 : }
282 :
283 0 : value = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET);
284 0 : data->digest_restart_calc = calculate_operation_digest(data->params_restart,
285 : value);
286 : }
287 :
288 : /*!
289 : * \internal
290 : * \brief Create a new digest cache entry with calculated digests
291 : *
292 : * \param[in,out] rsc Resource that action was for
293 : * \param[in] task Name of action performed
294 : * \param[in,out] interval_ms Action's interval (will be reset if in overrides)
295 : * \param[in] node Node action was performed on
296 : * \param[in] xml_op XML of operation in CIB status (if available)
297 : * \param[in] overrides Key/value table to override resource parameters
298 : * \param[in] calc_secure Whether to calculate secure digest
299 : * \param[in,out] scheduler Scheduler data
300 : *
301 : * \return Pointer to new digest cache entry (or NULL on memory error)
302 : * \note It is the caller's responsibility to free the result using
303 : * pe__free_digests().
304 : */
305 : pcmk__op_digest_t *
306 0 : pe__calculate_digests(pcmk_resource_t *rsc, const char *task,
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 0 : pcmk__op_digest_t *data = NULL;
312 0 : const char *op_version = NULL;
313 0 : GHashTable *params = NULL;
314 :
315 0 : CRM_CHECK(scheduler != NULL, return NULL);
316 :
317 0 : data = calloc(1, sizeof(pcmk__op_digest_t));
318 0 : if (data == NULL) {
319 0 : pcmk__sched_err("Could not allocate memory for operation digest");
320 0 : return NULL;
321 : }
322 :
323 0 : data->rc = pcmk__digest_match;
324 :
325 0 : if (xml_op != NULL) {
326 0 : op_version = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET);
327 : }
328 :
329 0 : if ((op_version == NULL) && (scheduler->input != NULL)) {
330 0 : op_version = crm_element_value(scheduler->input,
331 : PCMK_XA_CRM_FEATURE_SET);
332 : }
333 :
334 0 : if (op_version == NULL) {
335 0 : op_version = CRM_FEATURE_SET;
336 : }
337 :
338 0 : params = pe_rsc_params(rsc, node, scheduler);
339 0 : calculate_main_digest(data, rsc, node, params, task, interval_ms, xml_op,
340 : op_version, overrides, scheduler);
341 0 : if (calc_secure) {
342 0 : calculate_secure_digest(data, rsc, params, xml_op, op_version,
343 : overrides);
344 : }
345 0 : calculate_restart_digest(data, xml_op, op_version);
346 0 : return data;
347 : }
348 :
349 : /*!
350 : * \internal
351 : * \brief Calculate action digests and store in node's digest cache
352 : *
353 : * \param[in,out] rsc Resource that action was for
354 : * \param[in] task Name of action performed
355 : * \param[in] interval_ms Action's interval
356 : * \param[in,out] node Node action was performed on
357 : * \param[in] xml_op XML of operation in CIB status (if available)
358 : * \param[in] calc_secure Whether to calculate secure digest
359 : * \param[in,out] scheduler Scheduler data
360 : *
361 : * \return Pointer to node's digest cache entry
362 : */
363 : static pcmk__op_digest_t *
364 0 : rsc_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 0 : pcmk__op_digest_t *data = NULL;
369 0 : char *key = pcmk__op_key(rsc->id, task, interval_ms);
370 :
371 0 : data = g_hash_table_lookup(node->details->digest_cache, key);
372 0 : if (data == NULL) {
373 0 : data = pe__calculate_digests(rsc, task, &interval_ms, node, xml_op,
374 : NULL, calc_secure, scheduler);
375 0 : CRM_ASSERT(data != NULL);
376 0 : g_hash_table_insert(node->details->digest_cache, strdup(key), data);
377 : }
378 0 : free(key);
379 0 : return data;
380 : }
381 :
382 : /*!
383 : * \internal
384 : * \brief Calculate operation digests and compare against an XML history entry
385 : *
386 : * \param[in,out] rsc Resource to check
387 : * \param[in] xml_op Resource history XML
388 : * \param[in,out] node Node to use for digest calculation
389 : * \param[in,out] scheduler Scheduler data
390 : *
391 : * \return Pointer to node's digest cache entry, with comparison result set
392 : */
393 : pcmk__op_digest_t *
394 0 : rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op,
395 : pcmk_node_t *node, pcmk_scheduler_t *scheduler)
396 : {
397 0 : pcmk__op_digest_t *data = NULL;
398 0 : guint interval_ms = 0;
399 :
400 : const char *op_version;
401 0 : const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
402 : const char *digest_all;
403 : const char *digest_restart;
404 :
405 0 : CRM_ASSERT(node != NULL);
406 :
407 0 : op_version = crm_element_value(xml_op, PCMK_XA_CRM_FEATURE_SET);
408 0 : digest_all = crm_element_value(xml_op, PCMK__XA_OP_DIGEST);
409 0 : digest_restart = crm_element_value(xml_op, PCMK__XA_OP_RESTART_DIGEST);
410 :
411 0 : crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
412 0 : data = rsc_action_digest(rsc, task, interval_ms, node, xml_op,
413 0 : pcmk_is_set(scheduler->flags,
414 : pcmk_sched_sanitized),
415 : scheduler);
416 :
417 0 : if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
418 0 : 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,
424 : crm_element_value(xml_op, PCMK__XA_TRANSITION_MAGIC));
425 0 : data->rc = pcmk__digest_restart;
426 :
427 0 : } else if (digest_all == NULL) {
428 : /* it is unknown what the previous op digest was */
429 0 : data->rc = pcmk__digest_unknown;
430 :
431 0 : } 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 0 : if ((interval_ms == 0) && (data->rc == pcmk__digest_restart)) {
442 0 : 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,
450 : PCMK__XA_TRANSITION_MAGIC));
451 :
452 : } else {
453 0 : 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,
462 : PCMK__XA_TRANSITION_MAGIC));
463 0 : data->rc = pcmk__digest_mismatch;
464 : }
465 :
466 : } else {
467 0 : data->rc = pcmk__digest_match;
468 : }
469 0 : return data;
470 : }
471 :
472 : /*!
473 : * \internal
474 : * \brief Create an unfencing summary for use in special node attribute
475 : *
476 : * Create a string combining a fence device's resource ID, agent type, and
477 : * parameter digest (whether for all parameters or just non-private parameters).
478 : * This can be stored in a special node attribute, allowing us to detect changes
479 : * in either the agent type or parameters, to know whether unfencing must be
480 : * redone or can be safely skipped when the device's history is cleaned.
481 : *
482 : * \param[in] rsc_id Fence device resource ID
483 : * \param[in] agent_type Fence device agent
484 : * \param[in] param_digest Fence device parameter digest
485 : *
486 : * \return Newly allocated string with unfencing digest
487 : * \note The caller is responsible for freeing the result.
488 : */
489 : static inline char *
490 0 : create_unfencing_summary(const char *rsc_id, const char *agent_type,
491 : const char *param_digest)
492 : {
493 0 : return crm_strdup_printf("%s:%s:%s", rsc_id, agent_type, param_digest);
494 : }
495 :
496 : /*!
497 : * \internal
498 : * \brief Check whether a node can skip unfencing
499 : *
500 : * Check whether a fence device's current definition matches a node's
501 : * stored summary of when it was last unfenced by the device.
502 : *
503 : * \param[in] rsc_id Fence device's resource ID
504 : * \param[in] agent Fence device's agent type
505 : * \param[in] digest_calc Fence device's current parameter digest
506 : * \param[in] node_summary Value of node's special unfencing node attribute
507 : * (a comma-separated list of unfencing summaries for
508 : * all devices that have unfenced this node)
509 : *
510 : * \return TRUE if digest matches, FALSE otherwise
511 : */
512 : static bool
513 0 : unfencing_digest_matches(const char *rsc_id, const char *agent,
514 : const char *digest_calc, const char *node_summary)
515 : {
516 0 : bool matches = FALSE;
517 :
518 0 : if (rsc_id && agent && digest_calc && node_summary) {
519 0 : 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 0 : matches = (strstr(node_summary, search_secure) != NULL);
526 0 : crm_trace("Calculated unfencing digest '%s' %sfound in '%s'",
527 : search_secure, matches? "" : "not ", node_summary);
528 0 : free(search_secure);
529 : }
530 0 : 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 :
539 : /*!
540 : * \internal
541 : * \brief Calculate fence device digests and digest comparison result
542 : *
543 : * \param[in,out] rsc Fence device resource
544 : * \param[in] agent Fence device's agent type
545 : * \param[in,out] node Node with digest cache to use
546 : * \param[in,out] scheduler Scheduler data
547 : *
548 : * \return Node's digest cache entry
549 : */
550 : pcmk__op_digest_t *
551 0 : pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent,
552 : pcmk_node_t *node, pcmk_scheduler_t *scheduler)
553 : {
554 0 : const char *node_summary = NULL;
555 :
556 : // Calculate device's current parameter digests
557 0 : 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 0 : node_summary = pcmk__node_attr(node, CRM_ATTR_DIGESTS_ALL, NULL,
562 : pcmk__rsc_node_current);
563 0 : if (node_summary == NULL) {
564 0 : data->rc = pcmk__digest_unknown;
565 0 : return data;
566 : }
567 :
568 : // Check whether full parameter digest matches
569 0 : if (unfencing_digest_matches(rsc->id, agent, data->digest_all_calc,
570 : node_summary)) {
571 0 : data->rc = pcmk__digest_match;
572 0 : return data;
573 : }
574 :
575 : // Check whether secure parameter digest matches
576 0 : node_summary = pcmk__node_attr(node, CRM_ATTR_DIGESTS_SECURE, NULL,
577 : pcmk__rsc_node_current);
578 0 : if (unfencing_digest_matches(rsc->id, agent, data->digest_secure_calc,
579 : node_summary)) {
580 0 : data->rc = pcmk__digest_match;
581 0 : if (!pcmk__is_daemon && scheduler->priv != NULL) {
582 0 : pcmk__output_t *out = scheduler->priv;
583 0 : out->info(out, "Only 'private' parameters to %s "
584 : "for unfencing %s changed", rsc->id,
585 : pcmk__node_name(node));
586 : }
587 0 : return data;
588 : }
589 :
590 : // Parameters don't match
591 0 : data->rc = pcmk__digest_mismatch;
592 0 : if (pcmk_is_set(scheduler->flags, pcmk_sched_sanitized)
593 0 : && (data->digest_secure_calc != NULL)) {
594 :
595 0 : if (scheduler->priv != NULL) {
596 0 : pcmk__output_t *out = scheduler->priv;
597 0 : char *digest = create_unfencing_summary(rsc->id, agent,
598 0 : data->digest_secure_calc);
599 :
600 0 : out->info(out, "Parameters to %s for unfencing "
601 : "%s changed, try '%s'", rsc->id,
602 : pcmk__node_name(node), digest);
603 0 : free(digest);
604 0 : } else if (!pcmk__is_daemon) {
605 0 : char *digest = create_unfencing_summary(rsc->id, agent,
606 0 : data->digest_secure_calc);
607 :
608 0 : printf("Parameters to %s for unfencing %s changed, try '%s'\n",
609 : rsc->id, pcmk__node_name(node), digest);
610 0 : free(digest);
611 : }
612 : }
613 0 : return data;
614 : }
|