pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
operations.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 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 <regex.h>
17#include <stdio.h>
18#include <string.h>
19#include <stdlib.h>
20#include <sys/types.h>
21#include <ctype.h>
22
23#include <crm/crm.h>
24#include <crm/lrmd.h>
25#include <crm/msg_xml.h>
26#include <crm/common/xml.h>
28#include <crm/common/util.h>
29
30static regex_t *notify_migrate_re = NULL;
31
44char *
45pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
46{
47 CRM_ASSERT(rsc_id != NULL);
48 CRM_ASSERT(op_type != NULL);
49 return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
50}
51
52static inline gboolean
53convert_interval(const char *s, guint *interval_ms)
54{
55 unsigned long l;
56
57 errno = 0;
58 l = strtoul(s, NULL, 10);
59
60 if (errno != 0) {
61 return FALSE;
62 }
63
64 *interval_ms = (guint) l;
65 return TRUE;
66}
67
68static gboolean
69try_fast_match(const char *key, const char *underbar1, const char *underbar2,
70 char **rsc_id, char **op_type, guint *interval_ms)
71{
72 if (interval_ms) {
73 if (!convert_interval(underbar2+1, interval_ms)) {
74 return FALSE;
75 }
76 }
77
78 if (rsc_id) {
79 *rsc_id = strndup(key, underbar1-key);
80 }
81
82 if (op_type) {
83 *op_type = strndup(underbar1+1, underbar2-underbar1-1);
84 }
85
86 return TRUE;
87}
88
89static gboolean
90try_basic_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
91{
92 char *interval_sep = NULL;
93 char *type_sep = NULL;
94
95 // Parse interval at end of string
96 interval_sep = strrchr(key, '_');
97 if (interval_sep == NULL) {
98 return FALSE;
99 }
100
101 if (interval_ms) {
102 if (!convert_interval(interval_sep+1, interval_ms)) {
103 return FALSE;
104 }
105 }
106
107 type_sep = interval_sep-1;
108
109 while (1) {
110 if (*type_sep == '_') {
111 break;
112 } else if (type_sep == key) {
113 if (interval_ms) {
114 *interval_ms = 0;
115 }
116
117 return FALSE;
118 }
119
120 type_sep--;
121 }
122
123 if (op_type) {
124 // Add one here to skip the leading underscore we landed on in the
125 // while loop.
126 *op_type = strndup(type_sep+1, interval_sep-type_sep-1);
127 }
128
129 // Everything else is the name of the resource.
130 if (rsc_id) {
131 *rsc_id = strndup(key, type_sep-key);
132 }
133
134 return TRUE;
135}
136
137static gboolean
138try_migrate_notify_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
139{
140 int rc = 0;
141 size_t nmatch = 8;
142 regmatch_t pmatch[nmatch];
143
144 if (notify_migrate_re == NULL) {
145 // cppcheck-suppress memleak
146 notify_migrate_re = calloc(1, sizeof(regex_t));
147 rc = regcomp(notify_migrate_re, "^(.*)_(migrate_(from|to)|(pre|post)_notify_([a-z]+|migrate_(from|to)))_([0-9]+)$",
148 REG_EXTENDED);
149 CRM_ASSERT(rc == 0);
150 }
151
152 rc = regexec(notify_migrate_re, key, nmatch, pmatch, 0);
153 if (rc == REG_NOMATCH) {
154 return FALSE;
155 }
156
157 if (rsc_id) {
158 *rsc_id = strndup(key+pmatch[1].rm_so, pmatch[1].rm_eo-pmatch[1].rm_so);
159 }
160
161 if (op_type) {
162 *op_type = strndup(key+pmatch[2].rm_so, pmatch[2].rm_eo-pmatch[2].rm_so);
163 }
164
165 if (interval_ms) {
166 if (!convert_interval(key+pmatch[7].rm_so, interval_ms)) {
167 if (rsc_id) {
168 free(*rsc_id);
169 *rsc_id = NULL;
170 }
171
172 if (op_type) {
173 free(*op_type);
174 *op_type = NULL;
175 }
176
177 return FALSE;
178 }
179 }
180
181 return TRUE;
182}
183
184gboolean
185parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
186{
187 char *underbar1 = NULL;
188 char *underbar2 = NULL;
189 char *underbar3 = NULL;
190
191 // Initialize output variables in case of early return
192 if (rsc_id) {
193 *rsc_id = NULL;
194 }
195
196 if (op_type) {
197 *op_type = NULL;
198 }
199
200 if (interval_ms) {
201 *interval_ms = 0;
202 }
203
204 CRM_CHECK(key && *key, return FALSE);
205
206 underbar1 = strchr(key, '_');
207 if (!underbar1) {
208 return FALSE;
209 }
210
211 underbar2 = strchr(underbar1+1, '_');
212 if (!underbar2) {
213 return FALSE;
214 }
215
216 underbar3 = strchr(underbar2+1, '_');
217
218 if (!underbar3) {
219 return try_fast_match(key, underbar1, underbar2,
220 rsc_id, op_type, interval_ms);
221 } else if (try_migrate_notify_match(key, rsc_id, op_type, interval_ms)) {
222 return TRUE;
223 } else {
224 return try_basic_match(key, rsc_id, op_type, interval_ms);
225 }
226}
227
228char *
229pcmk__notify_key(const char *rsc_id, const char *notify_type,
230 const char *op_type)
231{
232 CRM_CHECK(rsc_id != NULL, return NULL);
233 CRM_CHECK(op_type != NULL, return NULL);
234 CRM_CHECK(notify_type != NULL, return NULL);
235 return crm_strdup_printf("%s_%s_notify_%s_0",
236 rsc_id, notify_type, op_type);
237}
238
254gboolean
255decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
256 int *op_status, int *op_rc, int *target_rc)
257{
258 int res = 0;
259 char *key = NULL;
260 gboolean result = TRUE;
261 int local_op_status = -1;
262 int local_op_rc = -1;
263
264 CRM_CHECK(magic != NULL, return FALSE);
265
266#ifdef HAVE_SSCANF_M
267 res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
268#else
269 key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
270 CRM_ASSERT(key);
271 res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
272#endif
273 if (res == EOF) {
274 crm_err("Could not decode transition information '%s': %s",
275 magic, pcmk_rc_str(errno));
276 result = FALSE;
277 } else if (res < 3) {
278 crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
279 magic, res);
280 result = FALSE;
281 } else {
282 if (op_status) {
283 *op_status = local_op_status;
284 }
285 if (op_rc) {
286 *op_rc = local_op_rc;
287 }
288 result = decode_transition_key(key, uuid, transition_id, action_id,
289 target_rc);
290 }
291 free(key);
292 return result;
293}
294
295char *
296pcmk__transition_key(int transition_id, int action_id, int target_rc,
297 const char *node)
298{
299 CRM_CHECK(node != NULL, return NULL);
300 return crm_strdup_printf("%d:%d:%d:%-*s",
301 action_id, transition_id, target_rc, 36, node);
302}
303
317gboolean
318decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
319 int *target_rc)
320{
321 int local_transition_id = -1;
322 int local_action_id = -1;
323 int local_target_rc = -1;
324 char local_uuid[37] = { '\0' };
325
326 // Initialize any supplied output arguments
327 if (uuid) {
328 *uuid = NULL;
329 }
330 if (transition_id) {
331 *transition_id = -1;
332 }
333 if (action_id) {
334 *action_id = -1;
335 }
336 if (target_rc) {
337 *target_rc = -1;
338 }
339
340 CRM_CHECK(key != NULL, return FALSE);
341 if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
342 &local_target_rc, local_uuid) != 4) {
343 crm_err("Invalid transition key '%s'", key);
344 return FALSE;
345 }
346 if (strlen(local_uuid) != 36) {
347 crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
348 }
349 if (uuid) {
350 *uuid = strdup(local_uuid);
351 CRM_ASSERT(*uuid);
352 }
353 if (transition_id) {
354 *transition_id = local_transition_id;
355 }
356 if (action_id) {
357 *action_id = local_action_id;
358 }
359 if (target_rc) {
360 *target_rc = local_target_rc;
361 }
362 return TRUE;
363}
364
365// Return true if a is an attribute that should be filtered
366static bool
367should_filter_for_digest(xmlAttrPtr a, void *user_data)
368{
369 if (strncmp((const char *) a->name, CRM_META "_",
370 sizeof(CRM_META " ") - 1) == 0) {
371 return true;
372 }
373 return pcmk__str_any_of((const char *) a->name,
379 "pcmk_external_ip",
380 NULL);
381}
382
389void
390pcmk__filter_op_for_digest(xmlNode *param_set)
391{
392 char *key = NULL;
393 char *timeout = NULL;
394 guint interval_ms = 0;
395
396 if (param_set == NULL) {
397 return;
398 }
399
400 /* Timeout is useful for recurring operation digests, so grab it before
401 * removing meta-attributes
402 */
404 if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
405 interval_ms = 0;
406 }
407 free(key);
408 key = NULL;
409 if (interval_ms != 0) {
411 timeout = crm_element_value_copy(param_set, key);
412 }
413
414 // Remove all CRM_meta_* attributes and certain other attributes
415 pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
416
417 // Add timeout back for recurring operation digests
418 if (timeout != NULL) {
419 crm_xml_add(param_set, key, timeout);
420 }
421 free(timeout);
422 free(key);
423}
424
425int
427{
428 int rc = 0;
429
430 if (op && op->user_data) {
431 decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
432 }
433 return rc;
434}
435
436gboolean
438{
439 switch (op->op_status) {
442 return FALSE;
443
446 case PCMK_EXEC_ERROR:
451 return TRUE;
452
453 default:
454 if (target_rc != op->rc) {
455 return TRUE;
456 }
457 }
458
459 return FALSE;
460}
461
473xmlNode *
474crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
475 const char *interval_spec, const char *timeout)
476{
477 xmlNode *xml_op;
478
479 CRM_CHECK(prefix && task && interval_spec, return NULL);
480
482 crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
483 crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
484 crm_xml_add(xml_op, "name", task);
485 if (timeout) {
487 }
488 return xml_op;
489}
490
500bool
501crm_op_needs_metadata(const char *rsc_class, const char *op)
502{
503 /* Agent metadata is used to determine whether an agent reload is possible,
504 * so if this op is not relevant to that feature, we don't need metadata.
505 */
506
507 CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
508
509 if ((rsc_class != NULL)
511 // Metadata is needed only for resource classes that use parameters
512 return false;
513 }
514 if (op == NULL) {
515 return true;
516 }
517
518 // Metadata is needed only for these actions
523 CRMD_ACTION_NOTIFY, NULL);
524}
525
534bool
536{
537 return pcmk__str_any_of(action, "off", "reboot", "poweroff", NULL);
538}
539
540bool
541pcmk_is_probe(const char *task, guint interval)
542{
543 if (task == NULL) {
544 return false;
545 }
546
547 return (interval == 0) && pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_none);
548}
549
550bool
551pcmk_xe_is_probe(xmlNode *xml_op)
552{
553 const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
554 const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS);
555 int interval_ms;
556
557 pcmk__scan_min_int(interval_ms_s, &interval_ms, 0);
558 return pcmk_is_probe(task, interval_ms);
559}
560
561bool
563{
564 int status = PCMK_EXEC_UNKNOWN;
565 int rc = PCMK_OCF_OK;
566
567 if (!pcmk_xe_is_probe(xml_op)) {
568 return false;
569 }
570
573
574 return rc == PCMK_OCF_NOT_INSTALLED || rc == PCMK_OCF_INVALID_PARAM ||
575 status == PCMK_EXEC_NOT_INSTALLED;
576}
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:59
const char * parent
Definition: cib.c:25
#define PCMK__OP_FMT
Definition: internal.h:168
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:360
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_err(fmt, args...)
Definition: logging.h:359
Resource agent executor.
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:118
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:313
#define XML_LRM_ATTR_OPSTATUS
Definition: msg_xml.h:310
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:303
#define XML_ATTR_ID
Definition: msg_xml.h:134
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:294
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:300
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:302
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:128
#define XML_ATTR_OP
Definition: msg_xml.h:140
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:311
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:298
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:553
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:610
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:714
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:323
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:185
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:390
bool pcmk_xe_is_probe(xmlNode *xml_op)
Definition: operations.c:551
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:229
bool pcmk_is_probe(const char *task, guint interval)
Definition: operations.c:541
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:318
bool pcmk_xe_mask_probe_failure(xmlNode *xml_op)
Definition: operations.c:562
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:45
bool pcmk__is_fencing_action(const char *action)
Definition: operations.c:535
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:474
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: operations.c:437
int rsc_op_expected_rc(lrmd_event_data_t *op)
Definition: operations.c:426
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: operations.c:296
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition: operations.c:501
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:255
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:476
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
Definition: results.h:169
@ PCMK_OCF_INVALID_PARAM
Parameter invalid (in local context)
Definition: results.h:166
@ PCMK_OCF_OK
Success.
Definition: results.h:164
#define pcmk_ok
Definition: results.h:68
@ PCMK_EXEC_CANCELLED
Action was cancelled.
Definition: results.h:313
@ PCMK_EXEC_NO_SECRETS
Necessary CIB secrets are unavailable.
Definition: results.h:323
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
Definition: results.h:319
@ PCMK_EXEC_INVALID
Action cannot be attempted (e.g. shutdown)
Definition: results.h:321
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition: results.h:316
@ PCMK_EXEC_NOT_SUPPORTED
Agent does not implement requested action.
Definition: results.h:315
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
Definition: results.h:314
@ PCMK_EXEC_PENDING
Action is in progress.
Definition: results.h:311
@ PCMK_EXEC_UNKNOWN
Used only to initialize variables.
Definition: results.h:310
@ PCMK_EXEC_NO_FENCE_DEVICE
No fence device is configured for target.
Definition: results.h:322
@ PCMK_EXEC_NOT_CONNECTED
No connection to executor.
Definition: results.h:320
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:952
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:749
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:682