pacemaker 2.1.6-6fdc9deea29
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
operations.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#ifndef _GNU_SOURCE
13# define _GNU_SOURCE
14#endif
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <ctype.h>
21
22#include <crm/crm.h>
23#include <crm/lrmd.h>
24#include <crm/msg_xml.h>
25#include <crm/common/xml.h>
27#include <crm/common/util.h>
28
41char *
42pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
43{
44 CRM_ASSERT(rsc_id != NULL);
45 CRM_ASSERT(op_type != NULL);
46 return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
47}
48
49static inline gboolean
50convert_interval(const char *s, guint *interval_ms)
51{
52 unsigned long l;
53
54 errno = 0;
55 l = strtoul(s, NULL, 10);
56
57 if (errno != 0) {
58 return FALSE;
59 }
60
61 *interval_ms = (guint) l;
62 return TRUE;
63}
64
76static size_t
77match_before(const char *key, size_t position, const char **matches)
78{
79 for (int i = 0; matches[i] != NULL; ++i) {
80 const size_t match_len = strlen(matches[i]);
81
82 // Must have at least X_MATCH before position
83 if (position > (match_len + 1)) {
84 const size_t possible = position - match_len - 1;
85
86 if ((key[possible] == '_')
87 && (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
88 return possible;
89 }
90 }
91 }
92 return 0;
93}
94
95gboolean
96parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
97{
98 guint local_interval_ms = 0;
99 const size_t key_len = (key == NULL)? 0 : strlen(key);
100
101 // Operation keys must be formatted as RSC_ACTION_INTERVAL
102 size_t action_underbar = 0; // Index in key of underbar before ACTION
103 size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
104 size_t possible = 0;
105
106 /* Underbar was a poor choice of separator since both RSC and ACTION can
107 * contain underbars. Here, list action names and name prefixes that can.
108 */
109 const char *actions_with_underbars[] = {
112 NULL
113 };
114 const char *action_prefixes_with_underbars[] = {
115 "pre_" CRMD_ACTION_NOTIFY,
116 "post_" CRMD_ACTION_NOTIFY,
117 "confirmed-pre_" CRMD_ACTION_NOTIFY,
118 "confirmed-post_" CRMD_ACTION_NOTIFY,
119 NULL,
120 };
121
122 // Initialize output variables in case of early return
123 if (rsc_id) {
124 *rsc_id = NULL;
125 }
126 if (op_type) {
127 *op_type = NULL;
128 }
129 if (interval_ms) {
130 *interval_ms = 0;
131 }
132
133 // RSC_ACTION_INTERVAL implies a minimum of 5 characters
134 if (key_len < 5) {
135 return FALSE;
136 }
137
138 // Find, parse, and validate interval
139 interval_underbar = key_len - 2;
140 while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
141 --interval_underbar;
142 }
143 if ((interval_underbar == 2)
144 || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
145 return FALSE;
146 }
147
148 // Find the base (OCF) action name, disregarding prefixes
149 action_underbar = match_before(key, interval_underbar,
150 actions_with_underbars);
151 if (action_underbar == 0) {
152 action_underbar = interval_underbar - 2;
153 while ((action_underbar > 0) && (key[action_underbar] != '_')) {
154 --action_underbar;
155 }
156 if (action_underbar == 0) {
157 return FALSE;
158 }
159 }
160 possible = match_before(key, action_underbar,
161 action_prefixes_with_underbars);
162 if (possible != 0) {
163 action_underbar = possible;
164 }
165
166 // Set output variables
167 if (rsc_id != NULL) {
168 *rsc_id = strndup(key, action_underbar);
169 CRM_ASSERT(*rsc_id != NULL);
170 }
171 if (op_type != NULL) {
172 *op_type = strndup(key + action_underbar + 1,
173 interval_underbar - action_underbar - 1);
174 CRM_ASSERT(*op_type != NULL);
175 }
176 if (interval_ms != NULL) {
177 *interval_ms = local_interval_ms;
178 }
179 return TRUE;
180}
181
182char *
183pcmk__notify_key(const char *rsc_id, const char *notify_type,
184 const char *op_type)
185{
186 CRM_CHECK(rsc_id != NULL, return NULL);
187 CRM_CHECK(op_type != NULL, return NULL);
188 CRM_CHECK(notify_type != NULL, return NULL);
189 return crm_strdup_printf("%s_%s_notify_%s_0",
190 rsc_id, notify_type, op_type);
191}
192
208gboolean
209decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
210 int *op_status, int *op_rc, int *target_rc)
211{
212 int res = 0;
213 char *key = NULL;
214 gboolean result = TRUE;
215 int local_op_status = -1;
216 int local_op_rc = -1;
217
218 CRM_CHECK(magic != NULL, return FALSE);
219
220#ifdef HAVE_SSCANF_M
221 res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
222#else
223 key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
224 CRM_ASSERT(key);
225 res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
226#endif
227 if (res == EOF) {
228 crm_err("Could not decode transition information '%s': %s",
229 magic, pcmk_rc_str(errno));
230 result = FALSE;
231 } else if (res < 3) {
232 crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
233 magic, res);
234 result = FALSE;
235 } else {
236 if (op_status) {
237 *op_status = local_op_status;
238 }
239 if (op_rc) {
240 *op_rc = local_op_rc;
241 }
242 result = decode_transition_key(key, uuid, transition_id, action_id,
243 target_rc);
244 }
245 free(key);
246 return result;
247}
248
249char *
250pcmk__transition_key(int transition_id, int action_id, int target_rc,
251 const char *node)
252{
253 CRM_CHECK(node != NULL, return NULL);
254 return crm_strdup_printf("%d:%d:%d:%-*s",
255 action_id, transition_id, target_rc, 36, node);
256}
257
271gboolean
272decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
273 int *target_rc)
274{
275 int local_transition_id = -1;
276 int local_action_id = -1;
277 int local_target_rc = -1;
278 char local_uuid[37] = { '\0' };
279
280 // Initialize any supplied output arguments
281 if (uuid) {
282 *uuid = NULL;
283 }
284 if (transition_id) {
285 *transition_id = -1;
286 }
287 if (action_id) {
288 *action_id = -1;
289 }
290 if (target_rc) {
291 *target_rc = -1;
292 }
293
294 CRM_CHECK(key != NULL, return FALSE);
295 if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
296 &local_target_rc, local_uuid) != 4) {
297 crm_err("Invalid transition key '%s'", key);
298 return FALSE;
299 }
300 if (strlen(local_uuid) != 36) {
301 crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
302 }
303 if (uuid) {
304 *uuid = strdup(local_uuid);
305 CRM_ASSERT(*uuid);
306 }
307 if (transition_id) {
308 *transition_id = local_transition_id;
309 }
310 if (action_id) {
311 *action_id = local_action_id;
312 }
313 if (target_rc) {
314 *target_rc = local_target_rc;
315 }
316 return TRUE;
317}
318
319// Return true if a is an attribute that should be filtered
320static bool
321should_filter_for_digest(xmlAttrPtr a, void *user_data)
322{
323 if (strncmp((const char *) a->name, CRM_META "_",
324 sizeof(CRM_META " ") - 1) == 0) {
325 return true;
326 }
327 return pcmk__str_any_of((const char *) a->name,
333 "pcmk_external_ip",
334 NULL);
335}
336
343void
344pcmk__filter_op_for_digest(xmlNode *param_set)
345{
346 char *key = NULL;
347 char *timeout = NULL;
348 guint interval_ms = 0;
349
350 if (param_set == NULL) {
351 return;
352 }
353
354 /* Timeout is useful for recurring operation digests, so grab it before
355 * removing meta-attributes
356 */
358 if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
359 interval_ms = 0;
360 }
361 free(key);
362 key = NULL;
363 if (interval_ms != 0) {
365 timeout = crm_element_value_copy(param_set, key);
366 }
367
368 // Remove all CRM_meta_* attributes and certain other attributes
369 pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
370
371 // Add timeout back for recurring operation digests
372 if (timeout != NULL) {
373 crm_xml_add(param_set, key, timeout);
374 }
375 free(timeout);
376 free(key);
377}
378
379int
381{
382 int rc = 0;
383
384 if (op && op->user_data) {
385 decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
386 }
387 return rc;
388}
389
390gboolean
392{
393 switch (op->op_status) {
396 return FALSE;
397
400 case PCMK_EXEC_ERROR:
405 return TRUE;
406
407 default:
408 if (target_rc != op->rc) {
409 return TRUE;
410 }
411 }
412
413 return FALSE;
414}
415
427xmlNode *
428crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
429 const char *interval_spec, const char *timeout)
430{
431 xmlNode *xml_op;
432
433 CRM_CHECK(prefix && task && interval_spec, return NULL);
434
436 crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
437 crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
438 crm_xml_add(xml_op, "name", task);
439 if (timeout) {
441 }
442 return xml_op;
443}
444
454bool
455crm_op_needs_metadata(const char *rsc_class, const char *op)
456{
457 /* Agent metadata is used to determine whether an agent reload is possible,
458 * so if this op is not relevant to that feature, we don't need metadata.
459 */
460
461 CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
462
463 if ((rsc_class != NULL)
465 // Metadata is needed only for resource classes that use parameters
466 return false;
467 }
468 if (op == NULL) {
469 return true;
470 }
471
472 // Metadata is needed only for these actions
477 CRMD_ACTION_NOTIFY, NULL);
478}
479
488bool
490{
491 return pcmk__str_any_of(action, "off", "reboot", "poweroff", NULL);
492}
493
494bool
495pcmk_is_probe(const char *task, guint interval)
496{
497 if (task == NULL) {
498 return false;
499 }
500
501 return (interval == 0) && pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_none);
502}
503
504bool
505pcmk_xe_is_probe(const xmlNode *xml_op)
506{
507 const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
508 const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS);
509 int interval_ms;
510
511 pcmk__scan_min_int(interval_ms_s, &interval_ms, 0);
512 return pcmk_is_probe(task, interval_ms);
513}
514
515bool
516pcmk_xe_mask_probe_failure(const xmlNode *xml_op)
517{
518 int status = PCMK_EXEC_UNKNOWN;
519 int rc = PCMK_OCF_OK;
520
521 if (!pcmk_xe_is_probe(xml_op)) {
522 return false;
523 }
524
527
528 return rc == PCMK_OCF_NOT_INSTALLED || rc == PCMK_OCF_INVALID_PARAM ||
529 status == PCMK_EXEC_NOT_INSTALLED;
530}
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition agents.c:31
@ pcmk_ra_cap_params
Definition agents.h:61
const char * parent
Definition cib.c:25
#define PCMK__OP_FMT
Definition internal.h:170
Utility functions.
char * crm_meta_name(const char *field)
Definition utils.c:468
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:121
A dumping ground.
#define CRMD_ACTION_NOTIFY
Definition crm.h:185
#define CRMD_ACTION_RELOAD_AGENT
Definition crm.h:170
#define CRMD_ACTION_MIGRATED
Definition crm.h:172
#define CRMD_ACTION_STATUS
Definition crm.h:188
#define CRM_META
Definition crm.h:78
#define CRMD_ACTION_DEMOTE
Definition crm.h:182
#define CRMD_ACTION_RELOAD
Definition crm.h:169
#define CRMD_ACTION_MIGRATE
Definition crm.h:171
#define CRMD_ACTION_START
Definition crm.h:174
#define CRMD_ACTION_PROMOTE
Definition crm.h:180
#define crm_warn(fmt, args...)
Definition logging.h:376
#define CRM_CHECK(expr, failure_action)
Definition logging.h:235
#define crm_err(fmt, args...)
Definition logging.h:375
Resource agent executor.
#define XML_ATTR_CRM_VERSION
Definition msg_xml.h:131
#define XML_LRM_ATTR_OP_DIGEST
Definition msg_xml.h:328
#define XML_LRM_ATTR_OPSTATUS
Definition msg_xml.h:325
#define XML_LRM_ATTR_TARGET_UUID
Definition msg_xml.h:318
#define XML_ATTR_ID
Definition msg_xml.h:147
#define XML_LRM_ATTR_INTERVAL
Definition msg_xml.h:309
#define XML_LRM_ATTR_TASK
Definition msg_xml.h:315
#define XML_LRM_ATTR_TARGET
Definition msg_xml.h:317
#define XML_ATTR_TIMEOUT
Definition msg_xml.h:141
#define XML_ATTR_OP
Definition msg_xml.h:153
#define XML_LRM_ATTR_RC
Definition msg_xml.h:326
#define XML_LRM_ATTR_INTERVAL_MS
Definition msg_xml.h:313
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:496
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition nvpair.c:532
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition nvpair.c:589
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition nvpair.c:693
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:302
bool pcmk_xe_mask_probe_failure(const xmlNode *xml_op)
Definition operations.c:516
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition operations.c:96
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition operations.c:344
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition operations.c:183
bool pcmk_is_probe(const char *task, guint interval)
Definition operations.c:495
gboolean decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id, int *target_rc)
Parse a transition key into its constituent parts.
Definition operations.c:272
int rsc_op_expected_rc(const lrmd_event_data_t *op)
Definition operations.c:380
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition operations.c:42
bool pcmk__is_fencing_action(const char *action)
Definition operations.c:489
xmlNode * crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task, const char *interval_spec, const char *timeout)
Create a CIB XML element for an operation.
Definition operations.c:428
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition operations.c:391
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition operations.c:250
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition operations.c:455
gboolean decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc)
Parse a transition magic string into its constituent parts.
Definition operations.c:209
bool pcmk_xe_is_probe(const xmlNode *xml_op)
Definition operations.c:505
unsigned int timeout
Definition pcmk_fence.c:32
const char * action
Definition pcmk_fence.c:30
pcmk__action_result_t result
Definition pcmk_fence.c:35
char * strndup(const char *str, size_t len)
#define CRM_ASSERT(expr)
Definition results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:488
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
Definition results.h:172
@ PCMK_OCF_INVALID_PARAM
Parameter invalid (in local context)
Definition results.h:169
@ PCMK_OCF_OK
Success.
Definition results.h:167
#define pcmk_ok
Definition results.h:68
@ PCMK_EXEC_CANCELLED
Action was cancelled.
Definition results.h:316
@ PCMK_EXEC_NO_SECRETS
Necessary CIB secrets are unavailable.
Definition results.h:326
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
Definition results.h:322
@ PCMK_EXEC_INVALID
Action cannot be attempted (e.g. shutdown)
Definition results.h:324
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition results.h:319
@ PCMK_EXEC_NOT_SUPPORTED
Agent does not implement requested action.
Definition results.h:318
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
Definition results.h:317
@ PCMK_EXEC_PENDING
Action is in progress.
Definition results.h:314
@ PCMK_EXEC_UNKNOWN
Used only to initialize variables.
Definition results.h:313
@ PCMK_EXEC_NO_FENCE_DEVICE
No fence device is configured for target.
Definition results.h:325
@ PCMK_EXEC_NOT_CONNECTED
No connection to executor.
Definition results.h:323
op_status
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
@ pcmk__str_none
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:957
const char * user_data
Definition lrmd.h:225
enum ocf_exitcode rc
Definition lrmd.h:239
Wrappers for and extensions to libxml2.
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:677
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition xml.c:618