pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
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 #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/common/xml.h>
26 #include <crm/common/util.h>
27 #include <crm/common/scheduler.h>
28 
36 const char *
38 {
39  switch (action) {
40  case pcmk_action_stop:
41  return PCMK_ACTION_STOP;
42 
44  return PCMK_ACTION_STOPPED;
45 
46  case pcmk_action_start:
47  return PCMK_ACTION_START;
48 
50  return PCMK_ACTION_RUNNING;
51 
54 
55  case pcmk_action_fence:
56  return PCMK_ACTION_STONITH;
57 
59  return PCMK_ACTION_MONITOR;
60 
61  case pcmk_action_notify:
62  return PCMK_ACTION_NOTIFY;
63 
65  return PCMK_ACTION_NOTIFIED;
66 
68  return PCMK_ACTION_PROMOTE;
69 
71  return PCMK_ACTION_PROMOTED;
72 
73  case pcmk_action_demote:
74  return PCMK_ACTION_DEMOTE;
75 
77  return PCMK_ACTION_DEMOTED;
78 
79  default: // pcmk_action_unspecified or invalid
80  return "no_action";
81  }
82 }
83 
91 enum action_tasks
92 pcmk_parse_action(const char *action_name)
93 {
94  if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
95  return pcmk_action_stop;
96 
97  } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOPPED, pcmk__str_none)) {
98  return pcmk_action_stopped;
99 
100  } else if (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
101  return pcmk_action_start;
102 
103  } else if (pcmk__str_eq(action_name, PCMK_ACTION_RUNNING, pcmk__str_none)) {
104  return pcmk_action_started;
105 
106  } else if (pcmk__str_eq(action_name, PCMK_ACTION_DO_SHUTDOWN,
107  pcmk__str_none)) {
108  return pcmk_action_shutdown;
109 
110  } else if (pcmk__str_eq(action_name, PCMK_ACTION_STONITH, pcmk__str_none)) {
111  return pcmk_action_fence;
112 
113  } else if (pcmk__str_eq(action_name, PCMK_ACTION_MONITOR, pcmk__str_none)) {
114  return pcmk_action_monitor;
115 
116  } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
117  return pcmk_action_notify;
118 
119  } else if (pcmk__str_eq(action_name, PCMK_ACTION_NOTIFIED,
120  pcmk__str_none)) {
121  return pcmk_action_notified;
122 
123  } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
124  return pcmk_action_promote;
125 
126  } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
127  return pcmk_action_demote;
128 
129  } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTED,
130  pcmk__str_none)) {
131  return pcmk_action_promoted;
132 
133  } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
134  return pcmk_action_demoted;
135  }
137 }
138 
146 const char *
148 {
149  switch (on_fail) {
150  case pcmk_on_fail_ignore:
151  return "ignore";
152 
153  case pcmk_on_fail_demote:
154  return "demote";
155 
156  case pcmk_on_fail_block:
157  return "block";
158 
160  return "recover";
161 
162  case pcmk_on_fail_ban:
163  return "migrate";
164 
165  case pcmk_on_fail_stop:
166  return "stop";
167 
169  return "fence";
170 
172  return "standby";
173 
175  return "restart-container";
176 
178  return "reset-remote";
179  }
180  return "<unknown>";
181 }
182 
195 char *
196 pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
197 {
198  pcmk__assert((rsc_id != NULL) && (op_type != NULL));
199  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
200 }
201 
202 static inline gboolean
203 convert_interval(const char *s, guint *interval_ms)
204 {
205  unsigned long l;
206 
207  errno = 0;
208  l = strtoul(s, NULL, 10);
209 
210  if (errno != 0) {
211  return FALSE;
212  }
213 
214  *interval_ms = (guint) l;
215  return TRUE;
216 }
217 
229 static size_t
230 match_before(const char *key, size_t position, const char **matches)
231 {
232  for (int i = 0; matches[i] != NULL; ++i) {
233  const size_t match_len = strlen(matches[i]);
234 
235  // Must have at least X_MATCH before position
236  if (position > (match_len + 1)) {
237  const size_t possible = position - match_len - 1;
238 
239  if ((key[possible] == '_')
240  && (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
241  return possible;
242  }
243  }
244  }
245  return 0;
246 }
247 
248 gboolean
249 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
250 {
251  guint local_interval_ms = 0;
252  const size_t key_len = (key == NULL)? 0 : strlen(key);
253 
254  // Operation keys must be formatted as RSC_ACTION_INTERVAL
255  size_t action_underbar = 0; // Index in key of underbar before ACTION
256  size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
257  size_t possible = 0;
258 
259  /* Underbar was a poor choice of separator since both RSC and ACTION can
260  * contain underbars. Here, list action names and name prefixes that can.
261  */
262  const char *actions_with_underbars[] = {
265  NULL
266  };
267  const char *action_prefixes_with_underbars[] = {
268  "pre_" PCMK_ACTION_NOTIFY,
269  "post_" PCMK_ACTION_NOTIFY,
270  "confirmed-pre_" PCMK_ACTION_NOTIFY,
271  "confirmed-post_" PCMK_ACTION_NOTIFY,
272  NULL,
273  };
274 
275  // Initialize output variables in case of early return
276  if (rsc_id) {
277  *rsc_id = NULL;
278  }
279  if (op_type) {
280  *op_type = NULL;
281  }
282  if (interval_ms) {
283  *interval_ms = 0;
284  }
285 
286  // RSC_ACTION_INTERVAL implies a minimum of 5 characters
287  if (key_len < 5) {
288  return FALSE;
289  }
290 
291  // Find, parse, and validate interval
292  interval_underbar = key_len - 2;
293  while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
294  --interval_underbar;
295  }
296  if ((interval_underbar == 2)
297  || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
298  return FALSE;
299  }
300 
301  // Find the base (OCF) action name, disregarding prefixes
302  action_underbar = match_before(key, interval_underbar,
303  actions_with_underbars);
304  if (action_underbar == 0) {
305  action_underbar = interval_underbar - 2;
306  while ((action_underbar > 0) && (key[action_underbar] != '_')) {
307  --action_underbar;
308  }
309  if (action_underbar == 0) {
310  return FALSE;
311  }
312  }
313  possible = match_before(key, action_underbar,
314  action_prefixes_with_underbars);
315  if (possible != 0) {
316  action_underbar = possible;
317  }
318 
319  // Set output variables
320  if (rsc_id != NULL) {
321  *rsc_id = strndup(key, action_underbar);
322  pcmk__mem_assert(*rsc_id);
323  }
324  if (op_type != NULL) {
325  *op_type = strndup(key + action_underbar + 1,
326  interval_underbar - action_underbar - 1);
327  pcmk__mem_assert(*op_type);
328  }
329  if (interval_ms != NULL) {
330  *interval_ms = local_interval_ms;
331  }
332  return TRUE;
333 }
334 
335 char *
336 pcmk__notify_key(const char *rsc_id, const char *notify_type,
337  const char *op_type)
338 {
339  CRM_CHECK(rsc_id != NULL, return NULL);
340  CRM_CHECK(op_type != NULL, return NULL);
341  CRM_CHECK(notify_type != NULL, return NULL);
342  return crm_strdup_printf("%s_%s_notify_%s_0",
343  rsc_id, notify_type, op_type);
344 }
345 
361 gboolean
362 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
363  int *op_status, int *op_rc, int *target_rc)
364 {
365  int res = 0;
366  char *key = NULL;
367  gboolean result = TRUE;
368  int local_op_status = -1;
369  int local_op_rc = -1;
370 
371  CRM_CHECK(magic != NULL, return FALSE);
372 
373 #ifdef HAVE_SSCANF_M
374  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
375 #else
376  // magic must have >=4 other characters
377  key = pcmk__assert_alloc(1, strlen(magic) - 3);
378  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
379 #endif
380  if (res == EOF) {
381  crm_err("Could not decode transition information '%s': %s",
382  magic, pcmk_rc_str(errno));
383  result = FALSE;
384  } else if (res < 3) {
385  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
386  magic, res);
387  result = FALSE;
388  } else {
389  if (op_status) {
390  *op_status = local_op_status;
391  }
392  if (op_rc) {
393  *op_rc = local_op_rc;
394  }
395  result = decode_transition_key(key, uuid, transition_id, action_id,
396  target_rc);
397  }
398  free(key);
399  return result;
400 }
401 
402 char *
403 pcmk__transition_key(int transition_id, int action_id, int target_rc,
404  const char *node)
405 {
406  CRM_CHECK(node != NULL, return NULL);
407  return crm_strdup_printf("%d:%d:%d:%-*s",
408  action_id, transition_id, target_rc, 36, node);
409 }
410 
424 gboolean
425 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
426  int *target_rc)
427 {
428  int local_transition_id = -1;
429  int local_action_id = -1;
430  int local_target_rc = -1;
431  char local_uuid[37] = { '\0' };
432 
433  // Initialize any supplied output arguments
434  if (uuid) {
435  *uuid = NULL;
436  }
437  if (transition_id) {
438  *transition_id = -1;
439  }
440  if (action_id) {
441  *action_id = -1;
442  }
443  if (target_rc) {
444  *target_rc = -1;
445  }
446 
447  CRM_CHECK(key != NULL, return FALSE);
448  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
449  &local_target_rc, local_uuid) != 4) {
450  crm_err("Invalid transition key '%s'", key);
451  return FALSE;
452  }
453  if (strlen(local_uuid) != 36) {
454  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
455  }
456  if (uuid) {
457  *uuid = pcmk__str_copy(local_uuid);
458  }
459  if (transition_id) {
460  *transition_id = local_transition_id;
461  }
462  if (action_id) {
463  *action_id = local_action_id;
464  }
465  if (target_rc) {
466  *target_rc = local_target_rc;
467  }
468  return TRUE;
469 }
470 
471 int
473 {
474  int rc = 0;
475 
476  if (op && op->user_data) {
477  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
478  }
479  return rc;
480 }
481 
482 gboolean
483 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
484 {
485  switch (op->op_status) {
486  case PCMK_EXEC_CANCELLED:
487  case PCMK_EXEC_PENDING:
488  return FALSE;
489 
491  case PCMK_EXEC_TIMEOUT:
492  case PCMK_EXEC_ERROR:
496  case PCMK_EXEC_INVALID:
497  return TRUE;
498 
499  default:
500  if (target_rc != op->rc) {
501  return TRUE;
502  }
503  }
504 
505  return FALSE;
506 }
507 
519 xmlNode *
520 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
521  const char *interval_spec, const char *timeout)
522 {
523  xmlNode *xml_op;
524 
525  CRM_CHECK(prefix && task && interval_spec, return NULL);
526 
527  xml_op = pcmk__xe_create(parent, PCMK_XE_OP);
528  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
529  crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec);
530  crm_xml_add(xml_op, PCMK_XA_NAME, task);
531  if (timeout) {
533  }
534  return xml_op;
535 }
536 
546 bool
547 crm_op_needs_metadata(const char *rsc_class, const char *op)
548 {
549  /* Agent metadata is used to determine whether an agent reload is possible,
550  * so if this op is not relevant to that feature, we don't need metadata.
551  */
552 
553  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
554 
555  if ((rsc_class != NULL)
557  // Metadata is needed only for resource classes that use parameters
558  return false;
559  }
560  if (op == NULL) {
561  return true;
562  }
563 
564  // Metadata is needed only for these actions
569  PCMK_ACTION_NOTIFY, NULL);
570 }
571 
581 bool
583 {
585  PCMK__ACTION_POWEROFF, NULL);
586 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
A dumping ground.
No connection to executor.
Definition: results.h:337
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:196
#define PCMK_XA_NAME
Definition: xml_names.h:330
const char * user_data
Definition: lrmd_events.h:45
enum action_tasks pcmk_parse_action(const char *action_name)
Parse an action type from an action name.
Definition: actions.c:92
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
#define PCMK_ACTION_MONITOR
Definition: actions.h:60
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:313
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:59
#define PCMK_ACTION_DO_SHUTDOWN
Definition: actions.h:51
Resource agent executor.
action_tasks
Definition: actions.h:83
Necessary CIB secrets are unavailable.
Definition: results.h:340
action_fail_response
Definition: actions.h:130
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: actions.c:403
#define PCMK_ACTION_RELOAD
Definition: actions.h:69
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: actions.c:483
enum ocf_exitcode rc
Definition: lrmd_events.h:63
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:520
Action did not complete in time.
Definition: results.h:331
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:503
Action was cancelled.
Definition: results.h:330
No fence device is configured for target.
Definition: results.h:339
const char * action
Definition: pcmk_fence.c:30
Scheduler API.
#define PCMK_ACTION_RELOAD_AGENT
Definition: actions.h:70
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
#define PCMK_ACTION_REBOOT
Definition: actions.h:68
#define crm_warn(fmt, args...)
Definition: logging.h:394
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: actions.c:336
op_status
Utility functions.
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:94
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
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:425
#define PCMK_ACTION_START
Definition: actions.h:72
Wrappers for and extensions to libxml2.
int rsc_op_expected_rc(const lrmd_event_data_t *op)
Definition: actions.c:472
#define PCMK_ACTION_STOP
Definition: actions.h:75
#define PCMK_ACTION_STONITH
Definition: actions.h:74
#define PCMK_ACTION_NOTIFIED
Definition: actions.h:61
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:249
#define PCMK__ACTION_POWEROFF
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition: actions.c:547
const char * pcmk_action_text(enum action_tasks action)
Get string equivalent of an action type.
Definition: actions.c:37
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
#define pcmk__str_copy(str)
#define PCMK_META_TIMEOUT
Definition: options.h:114
const char * pcmk_on_fail_text(enum action_fail_response on_fail)
Get string equivalent of a failure handling type.
Definition: actions.c:147
#define pcmk__assert(expr)
#define PCMK_XE_OP
Definition: xml_names.h:146
#define PCMK_META_INTERVAL
Definition: options.h:91
Agent does not implement requested action.
Definition: results.h:332
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define crm_err(fmt, args...)
Definition: logging.h:391
#define PCMK_ACTION_STOPPED
Definition: actions.h:76
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:58
#define pcmk__mem_assert(ptr)
#define PCMK_ACTION_PROMOTE
Definition: actions.h:66
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:362
Action is in progress.
Definition: results.h:328
#define PCMK_ACTION_PROMOTED
Definition: actions.h:67
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
#define PCMK_ACTION_OFF
Definition: actions.h:63
#define PCMK_ACTION_RUNNING
Definition: actions.h:71
const char * parent
Definition: cib.c:27
Action cannot be attempted (e.g. shutdown)
Definition: results.h:338
#define PCMK_ACTION_DEMOTED
Definition: actions.h:50
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
bool pcmk__is_fencing_action(const char *action)
Definition: actions.c:582
unsigned int timeout
Definition: pcmk_fence.c:32
Execution failed, may be retried.
Definition: results.h:333
#define PCMK_ACTION_NOTIFY
Definition: actions.h:62