pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
actions.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 <stdio.h>
13#include <string.h>
14#include <stdlib.h>
15#include <sys/types.h>
16#include <ctype.h>
17
18#include <crm/crm.h>
19#include <crm/lrmd.h>
20#include <crm/common/xml.h>
22#include <crm/common/util.h>
24
33const char *
35{
36 switch (action) {
38 return PCMK_ACTION_STOP;
39
42
44 return PCMK_ACTION_START;
45
48
51
54
57
59 return PCMK_ACTION_NOTIFY;
60
63
66
69
71 return PCMK_ACTION_DEMOTE;
72
75
76 default: // pcmk__action_unspecified or invalid
77 return "no_action";
78 }
79}
80
90pcmk__parse_action(const char *action_name)
91{
92 if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
93 return pcmk__action_stop;
94
95 } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOPPED, pcmk__str_none)) {
97
98 } else if (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
99 return pcmk__action_start;
100
101 } else if (pcmk__str_eq(action_name, PCMK_ACTION_RUNNING, pcmk__str_none)) {
103
104 } else if (pcmk__str_eq(action_name, PCMK_ACTION_DO_SHUTDOWN,
107
108 } else if (pcmk__str_eq(action_name, PCMK_ACTION_STONITH, pcmk__str_none)) {
109 return pcmk__action_fence;
110
111 } else if (pcmk__str_eq(action_name, PCMK_ACTION_MONITOR, pcmk__str_none)) {
113
114 } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
115 return pcmk__action_notify;
116
117 } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFIED,
120
121 } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
123
124 } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
125 return pcmk__action_demote;
126
127 } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTED,
130
131 } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
133 }
135}
136
145const char *
147{
148 switch (on_fail) {
150 return "ignore";
151
153 return "demote";
154
156 return "block";
157
159 return "recover";
160
162 return "migrate";
163
165 return "stop";
166
168 return "fence";
169
171 return "standby";
172
174 return "restart-container";
175
177 return "reset-remote";
178 }
179 return "<unknown>";
180}
181
188void
189pcmk__free_action(gpointer user_data)
190{
191 pcmk_action_t *action = user_data;
192
193 if (action == NULL) {
194 return;
195 }
196 g_list_free_full(action->actions_before, free);
197 g_list_free_full(action->actions_after, free);
198 if (action->extra != NULL) {
199 g_hash_table_destroy(action->extra);
200 }
201 if (action->meta != NULL) {
202 g_hash_table_destroy(action->meta);
203 }
205 free(action->cancel_task);
206 free(action->reason);
207 free(action->task);
208 free(action->uuid);
209 free(action);
210}
211
224char *
225pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
226{
227 pcmk__assert((rsc_id != NULL) && (op_type != NULL));
228 return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
229}
230
231static inline gboolean
232convert_interval(const char *s, guint *interval_ms)
233{
234 unsigned long l;
235
236 errno = 0;
237 l = strtoul(s, NULL, 10);
238
239 if (errno != 0) {
240 return FALSE;
241 }
242
243 *interval_ms = (guint) l;
244 return TRUE;
245}
246
258static size_t
259match_before(const char *key, size_t position, const char **matches)
260{
261 for (int i = 0; matches[i] != NULL; ++i) {
262 const size_t match_len = strlen(matches[i]);
263
264 // Must have at least X_MATCH before position
265 if (position > (match_len + 1)) {
266 const size_t possible = position - match_len - 1;
267
268 if ((key[possible] == '_')
269 && (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
270 return possible;
271 }
272 }
273 }
274 return 0;
275}
276
277gboolean
278parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
279{
280 guint local_interval_ms = 0;
281 const size_t key_len = (key == NULL)? 0 : strlen(key);
282
283 // Operation keys must be formatted as RSC_ACTION_INTERVAL
284 size_t action_underbar = 0; // Index in key of underbar before ACTION
285 size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
286 size_t possible = 0;
287
288 /* Underbar was a poor choice of separator since both RSC and ACTION can
289 * contain underbars. Here, list action names and name prefixes that can.
290 */
291 const char *actions_with_underbars[] = {
294 NULL
295 };
296 const char *action_prefixes_with_underbars[] = {
297 "pre_" PCMK_ACTION_NOTIFY,
298 "post_" PCMK_ACTION_NOTIFY,
299 "confirmed-pre_" PCMK_ACTION_NOTIFY,
300 "confirmed-post_" PCMK_ACTION_NOTIFY,
301 NULL,
302 };
303
304 // Initialize output variables in case of early return
305 if (rsc_id) {
306 *rsc_id = NULL;
307 }
308 if (op_type) {
309 *op_type = NULL;
310 }
311 if (interval_ms) {
312 *interval_ms = 0;
313 }
314
315 // RSC_ACTION_INTERVAL implies a minimum of 5 characters
316 if (key_len < 5) {
317 return FALSE;
318 }
319
320 // Find, parse, and validate interval
321 interval_underbar = key_len - 2;
322 while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
323 --interval_underbar;
324 }
325 if ((interval_underbar == 2)
326 || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
327 return FALSE;
328 }
329
330 // Find the base (OCF) action name, disregarding prefixes
331 action_underbar = match_before(key, interval_underbar,
332 actions_with_underbars);
333 if (action_underbar == 0) {
334 action_underbar = interval_underbar - 2;
335 while ((action_underbar > 0) && (key[action_underbar] != '_')) {
336 --action_underbar;
337 }
338 if (action_underbar == 0) {
339 return FALSE;
340 }
341 }
342 possible = match_before(key, action_underbar,
343 action_prefixes_with_underbars);
344 if (possible != 0) {
345 action_underbar = possible;
346 }
347
348 // Set output variables
349 if (rsc_id != NULL) {
350 *rsc_id = strndup(key, action_underbar);
351 pcmk__mem_assert(*rsc_id);
352 }
353 if (op_type != NULL) {
354 *op_type = strndup(key + action_underbar + 1,
355 interval_underbar - action_underbar - 1);
356 pcmk__mem_assert(*op_type);
357 }
358 if (interval_ms != NULL) {
359 *interval_ms = local_interval_ms;
360 }
361 return TRUE;
362}
363
364char *
365pcmk__notify_key(const char *rsc_id, const char *notify_type,
366 const char *op_type)
367{
368 CRM_CHECK(rsc_id != NULL, return NULL);
369 CRM_CHECK(op_type != NULL, return NULL);
370 CRM_CHECK(notify_type != NULL, return NULL);
371 return crm_strdup_printf("%s_%s_notify_%s_0",
372 rsc_id, notify_type, op_type);
373}
374
390gboolean
391decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
392 int *op_status, int *op_rc, int *target_rc)
393{
394 int res = 0;
395 char *key = NULL;
396 gboolean result = TRUE;
397 int local_op_status = -1;
398 int local_op_rc = -1;
399
400 CRM_CHECK(magic != NULL, return FALSE);
401
402#ifdef HAVE_SSCANF_M
403 res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
404#else
405 // magic must have >=4 other characters
406 key = pcmk__assert_alloc(1, strlen(magic) - 3);
407 res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
408#endif
409 if (res == EOF) {
410 crm_err("Could not decode transition information '%s': %s",
411 magic, pcmk_rc_str(errno));
412 result = FALSE;
413 } else if (res < 3) {
414 crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
415 magic, res);
416 result = FALSE;
417 } else {
418 if (op_status) {
419 *op_status = local_op_status;
420 }
421 if (op_rc) {
422 *op_rc = local_op_rc;
423 }
424 result = decode_transition_key(key, uuid, transition_id, action_id,
425 target_rc);
426 }
427 free(key);
428 return result;
429}
430
431char *
432pcmk__transition_key(int transition_id, int action_id, int target_rc,
433 const char *node)
434{
435 CRM_CHECK(node != NULL, return NULL);
436 return crm_strdup_printf("%d:%d:%d:%-*s",
437 action_id, transition_id, target_rc, 36, node);
438}
439
453gboolean
454decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
455 int *target_rc)
456{
457 int local_transition_id = -1;
458 int local_action_id = -1;
459 int local_target_rc = -1;
460 char local_uuid[37] = { '\0' };
461
462 // Initialize any supplied output arguments
463 if (uuid) {
464 *uuid = NULL;
465 }
466 if (transition_id) {
467 *transition_id = -1;
468 }
469 if (action_id) {
470 *action_id = -1;
471 }
472 if (target_rc) {
473 *target_rc = -1;
474 }
475
476 CRM_CHECK(key != NULL, return FALSE);
477 if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
478 &local_target_rc, local_uuid) != 4) {
479 crm_err("Invalid transition key '%s'", key);
480 return FALSE;
481 }
482 if (strlen(local_uuid) != 36) {
483 crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
484 }
485 if (uuid) {
486 *uuid = pcmk__str_copy(local_uuid);
487 }
488 if (transition_id) {
489 *transition_id = local_transition_id;
490 }
491 if (action_id) {
492 *action_id = local_action_id;
493 }
494 if (target_rc) {
495 *target_rc = local_target_rc;
496 }
497 return TRUE;
498}
499
500int
502{
503 int rc = 0;
504
505 if (op && op->user_data) {
506 decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
507 }
508 return rc;
509}
510
511gboolean
513{
514 switch (op->op_status) {
517 return FALSE;
518
521 case PCMK_EXEC_ERROR:
526 return TRUE;
527
528 default:
529 if (target_rc != op->rc) {
530 return TRUE;
531 }
532 }
533
534 return FALSE;
535}
536
548xmlNode *
549crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
550 const char *interval_spec, const char *timeout)
551{
552 xmlNode *xml_op;
553
554 CRM_CHECK(prefix && task && interval_spec, return NULL);
555
557 pcmk__xe_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
558 crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec);
559 crm_xml_add(xml_op, PCMK_XA_NAME, task);
560 if (timeout) {
562 }
563 return xml_op;
564}
565
575bool
576crm_op_needs_metadata(const char *rsc_class, const char *op)
577{
578 /* Agent metadata is used to determine whether an agent reload is possible,
579 * so if this op is not relevant to that feature, we don't need metadata.
580 */
581
582 CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
583
584 if ((rsc_class != NULL)
586 // Metadata is needed only for resource classes that use parameters
587 return false;
588 }
589 if (op == NULL) {
590 return true;
591 }
592
593 // Metadata is needed only for these actions
598 PCMK_ACTION_NOTIFY, NULL);
599}
600
610bool
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition actions.c:278
void pcmk__free_action(gpointer user_data)
Definition actions.c:189
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition actions.c:365
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 actions.c:454
int rsc_op_expected_rc(const lrmd_event_data_t *op)
Definition actions.c:501
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:225
bool pcmk__is_fencing_action(const char *action)
Definition actions.c:611
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 actions.c:549
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition actions.c:512
const char * pcmk__action_text(enum pcmk__action_type action)
Definition actions.c:34
enum pcmk__action_type pcmk__parse_action(const char *action_name)
Definition actions.c:90
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition actions.c:432
const char * pcmk__on_fail_text(enum pcmk__on_fail on_fail)
Definition actions.c:146
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition actions.c:576
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 actions.c:391
#define PCMK_ACTION_PROMOTED
Definition actions.h:58
#define PCMK_ACTION_STOP
Definition actions.h:66
#define PCMK_ACTION_RUNNING
Definition actions.h:62
#define PCMK_ACTION_REBOOT
Definition actions.h:59
#define PCMK_ACTION_PROMOTE
Definition actions.h:57
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_NOTIFIED
Definition actions.h:52
#define PCMK_ACTION_MIGRATE_FROM
Definition actions.h:49
#define PCMK_ACTION_RELOAD
Definition actions.h:60
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:50
#define PCMK_ACTION_STOPPED
Definition actions.h:67
#define PCMK_ACTION_MONITOR
Definition actions.h:51
#define PCMK_ACTION_STONITH
Definition actions.h:65
#define PCMK_ACTION_RELOAD_AGENT
Definition actions.h:61
#define PCMK_ACTION_DEMOTED
Definition actions.h:41
#define PCMK_ACTION_OFF
Definition actions.h:54
#define PCMK_ACTION_DEMOTE
Definition actions.h:40
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:42
#define PCMK_ACTION_NOTIFY
Definition actions.h:53
pcmk__on_fail
@ pcmk__on_fail_stop
@ pcmk__on_fail_reset_remote
@ pcmk__on_fail_restart_container
@ pcmk__on_fail_ban
@ pcmk__on_fail_fence_node
@ pcmk__on_fail_block
@ pcmk__on_fail_restart
@ pcmk__on_fail_ignore
@ pcmk__on_fail_demote
@ pcmk__on_fail_standby_node
pcmk__action_type
@ pcmk__action_stop
@ pcmk__action_started
@ pcmk__action_monitor
@ pcmk__action_fence
@ pcmk__action_stopped
@ pcmk__action_notify
@ pcmk__action_start
@ pcmk__action_demote
@ pcmk__action_promote
@ pcmk__action_shutdown
@ pcmk__action_notified
@ pcmk__action_unspecified
@ pcmk__action_promoted
@ pcmk__action_demoted
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition agents.c:27
@ pcmk_ra_cap_params
Definition agents.h:57
const char * parent
Definition cib.c:27
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
Utility functions.
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
A dumping ground.
#define crm_warn(fmt, args...)
Definition logging.h:360
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_err(fmt, args...)
Definition logging.h:357
Resource agent executor.
void pcmk__free_node_copy(void *data)
Definition nodes.c:64
#define PCMK_META_INTERVAL
Definition options.h:92
#define PCMK_META_TIMEOUT
Definition options.h:115
unsigned int timeout
Definition pcmk_fence.c:34
const char * action
Definition pcmk_fence.c:32
pcmk__action_result_t result
Definition pcmk_fence.c:37
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ PCMK_EXEC_CANCELLED
Action was cancelled.
Definition results.h:312
@ PCMK_EXEC_NO_SECRETS
Necessary CIB secrets are unavailable.
Definition results.h:322
@ PCMK_EXEC_INVALID
Action cannot be attempted (e.g. shutdown)
Definition results.h:320
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition results.h:315
@ PCMK_EXEC_NOT_SUPPORTED
Agent does not implement requested action.
Definition results.h:314
@ PCMK_EXEC_TIMEOUT
Action did not complete in time.
Definition results.h:313
@ PCMK_EXEC_PENDING
Action is in progress.
Definition results.h:310
@ PCMK_EXEC_NO_FENCE_DEVICE
No fence device is configured for target.
Definition results.h:321
@ PCMK_EXEC_NOT_CONNECTED
No connection to executor.
Definition results.h:319
#define pcmk__assert(expr)
#define pcmk__mem_assert(ptr)
Scheduler API.
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
@ pcmk__str_none
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
#define pcmk__str_copy(str)
const char * user_data
Definition lrmd_events.h:47
enum ocf_exitcode rc
Definition lrmd_events.h:65
Wrappers for and extensions to libxml2.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
void pcmk__xe_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
#define PCMK_XA_NAME
Definition xml_names.h:330
#define PCMK_XE_OP
Definition xml_names.h:146