pacemaker  3.0.0-d8340737c4
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 #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>
23 #include <crm/common/scheduler.h>
24 
33 const char *
35 {
36  switch (action) {
37  case pcmk__action_stop:
38  return PCMK_ACTION_STOP;
39 
41  return PCMK_ACTION_STOPPED;
42 
43  case pcmk__action_start:
44  return PCMK_ACTION_START;
45 
47  return PCMK_ACTION_RUNNING;
48 
51 
52  case pcmk__action_fence:
53  return PCMK_ACTION_STONITH;
54 
56  return PCMK_ACTION_MONITOR;
57 
59  return PCMK_ACTION_NOTIFY;
60 
62  return PCMK_ACTION_NOTIFIED;
63 
65  return PCMK_ACTION_PROMOTE;
66 
68  return PCMK_ACTION_PROMOTED;
69 
71  return PCMK_ACTION_DEMOTE;
72 
74  return PCMK_ACTION_DEMOTED;
75 
76  default: // pcmk__action_unspecified or invalid
77  return "no_action";
78  }
79 }
80 
90 pcmk__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)) {
96  return pcmk__action_stopped;
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)) {
102  return pcmk__action_started;
103 
104  } else if (pcmk__str_eq(action_name, PCMK_ACTION_DO_SHUTDOWN,
105  pcmk__str_none)) {
106  return pcmk__action_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)) {
112  return pcmk__action_monitor;
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,
118  pcmk__str_none)) {
119  return pcmk__action_notified;
120 
121  } else if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
122  return pcmk__action_promote;
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,
128  pcmk__str_none)) {
129  return pcmk__action_promoted;
130 
131  } else if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
132  return pcmk__action_demoted;
133  }
135 }
136 
145 const char *
147 {
148  switch (on_fail) {
150  return "ignore";
151 
153  return "demote";
154 
155  case pcmk__on_fail_block:
156  return "block";
157 
159  return "recover";
160 
161  case pcmk__on_fail_ban:
162  return "migrate";
163 
164  case pcmk__on_fail_stop:
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 
194 char *
195 pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
196 {
197  pcmk__assert((rsc_id != NULL) && (op_type != NULL));
198  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
199 }
200 
201 static inline gboolean
202 convert_interval(const char *s, guint *interval_ms)
203 {
204  unsigned long l;
205 
206  errno = 0;
207  l = strtoul(s, NULL, 10);
208 
209  if (errno != 0) {
210  return FALSE;
211  }
212 
213  *interval_ms = (guint) l;
214  return TRUE;
215 }
216 
228 static size_t
229 match_before(const char *key, size_t position, const char **matches)
230 {
231  for (int i = 0; matches[i] != NULL; ++i) {
232  const size_t match_len = strlen(matches[i]);
233 
234  // Must have at least X_MATCH before position
235  if (position > (match_len + 1)) {
236  const size_t possible = position - match_len - 1;
237 
238  if ((key[possible] == '_')
239  && (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
240  return possible;
241  }
242  }
243  }
244  return 0;
245 }
246 
247 gboolean
248 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
249 {
250  guint local_interval_ms = 0;
251  const size_t key_len = (key == NULL)? 0 : strlen(key);
252 
253  // Operation keys must be formatted as RSC_ACTION_INTERVAL
254  size_t action_underbar = 0; // Index in key of underbar before ACTION
255  size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
256  size_t possible = 0;
257 
258  /* Underbar was a poor choice of separator since both RSC and ACTION can
259  * contain underbars. Here, list action names and name prefixes that can.
260  */
261  const char *actions_with_underbars[] = {
264  NULL
265  };
266  const char *action_prefixes_with_underbars[] = {
267  "pre_" PCMK_ACTION_NOTIFY,
268  "post_" PCMK_ACTION_NOTIFY,
269  "confirmed-pre_" PCMK_ACTION_NOTIFY,
270  "confirmed-post_" PCMK_ACTION_NOTIFY,
271  NULL,
272  };
273 
274  // Initialize output variables in case of early return
275  if (rsc_id) {
276  *rsc_id = NULL;
277  }
278  if (op_type) {
279  *op_type = NULL;
280  }
281  if (interval_ms) {
282  *interval_ms = 0;
283  }
284 
285  // RSC_ACTION_INTERVAL implies a minimum of 5 characters
286  if (key_len < 5) {
287  return FALSE;
288  }
289 
290  // Find, parse, and validate interval
291  interval_underbar = key_len - 2;
292  while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
293  --interval_underbar;
294  }
295  if ((interval_underbar == 2)
296  || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
297  return FALSE;
298  }
299 
300  // Find the base (OCF) action name, disregarding prefixes
301  action_underbar = match_before(key, interval_underbar,
302  actions_with_underbars);
303  if (action_underbar == 0) {
304  action_underbar = interval_underbar - 2;
305  while ((action_underbar > 0) && (key[action_underbar] != '_')) {
306  --action_underbar;
307  }
308  if (action_underbar == 0) {
309  return FALSE;
310  }
311  }
312  possible = match_before(key, action_underbar,
313  action_prefixes_with_underbars);
314  if (possible != 0) {
315  action_underbar = possible;
316  }
317 
318  // Set output variables
319  if (rsc_id != NULL) {
320  *rsc_id = strndup(key, action_underbar);
321  pcmk__mem_assert(*rsc_id);
322  }
323  if (op_type != NULL) {
324  *op_type = strndup(key + action_underbar + 1,
325  interval_underbar - action_underbar - 1);
326  pcmk__mem_assert(*op_type);
327  }
328  if (interval_ms != NULL) {
329  *interval_ms = local_interval_ms;
330  }
331  return TRUE;
332 }
333 
334 char *
335 pcmk__notify_key(const char *rsc_id, const char *notify_type,
336  const char *op_type)
337 {
338  CRM_CHECK(rsc_id != NULL, return NULL);
339  CRM_CHECK(op_type != NULL, return NULL);
340  CRM_CHECK(notify_type != NULL, return NULL);
341  return crm_strdup_printf("%s_%s_notify_%s_0",
342  rsc_id, notify_type, op_type);
343 }
344 
360 gboolean
361 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
362  int *op_status, int *op_rc, int *target_rc)
363 {
364  int res = 0;
365  char *key = NULL;
366  gboolean result = TRUE;
367  int local_op_status = -1;
368  int local_op_rc = -1;
369 
370  CRM_CHECK(magic != NULL, return FALSE);
371 
372 #ifdef HAVE_SSCANF_M
373  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
374 #else
375  // magic must have >=4 other characters
376  key = pcmk__assert_alloc(1, strlen(magic) - 3);
377  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
378 #endif
379  if (res == EOF) {
380  crm_err("Could not decode transition information '%s': %s",
381  magic, pcmk_rc_str(errno));
382  result = FALSE;
383  } else if (res < 3) {
384  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
385  magic, res);
386  result = FALSE;
387  } else {
388  if (op_status) {
389  *op_status = local_op_status;
390  }
391  if (op_rc) {
392  *op_rc = local_op_rc;
393  }
394  result = decode_transition_key(key, uuid, transition_id, action_id,
395  target_rc);
396  }
397  free(key);
398  return result;
399 }
400 
401 char *
402 pcmk__transition_key(int transition_id, int action_id, int target_rc,
403  const char *node)
404 {
405  CRM_CHECK(node != NULL, return NULL);
406  return crm_strdup_printf("%d:%d:%d:%-*s",
407  action_id, transition_id, target_rc, 36, node);
408 }
409 
423 gboolean
424 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
425  int *target_rc)
426 {
427  int local_transition_id = -1;
428  int local_action_id = -1;
429  int local_target_rc = -1;
430  char local_uuid[37] = { '\0' };
431 
432  // Initialize any supplied output arguments
433  if (uuid) {
434  *uuid = NULL;
435  }
436  if (transition_id) {
437  *transition_id = -1;
438  }
439  if (action_id) {
440  *action_id = -1;
441  }
442  if (target_rc) {
443  *target_rc = -1;
444  }
445 
446  CRM_CHECK(key != NULL, return FALSE);
447  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
448  &local_target_rc, local_uuid) != 4) {
449  crm_err("Invalid transition key '%s'", key);
450  return FALSE;
451  }
452  if (strlen(local_uuid) != 36) {
453  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
454  }
455  if (uuid) {
456  *uuid = pcmk__str_copy(local_uuid);
457  }
458  if (transition_id) {
459  *transition_id = local_transition_id;
460  }
461  if (action_id) {
462  *action_id = local_action_id;
463  }
464  if (target_rc) {
465  *target_rc = local_target_rc;
466  }
467  return TRUE;
468 }
469 
470 int
472 {
473  int rc = 0;
474 
475  if (op && op->user_data) {
476  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
477  }
478  return rc;
479 }
480 
481 gboolean
482 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
483 {
484  switch (op->op_status) {
485  case PCMK_EXEC_CANCELLED:
486  case PCMK_EXEC_PENDING:
487  return FALSE;
488 
490  case PCMK_EXEC_TIMEOUT:
491  case PCMK_EXEC_ERROR:
495  case PCMK_EXEC_INVALID:
496  return TRUE;
497 
498  default:
499  if (target_rc != op->rc) {
500  return TRUE;
501  }
502  }
503 
504  return FALSE;
505 }
506 
518 xmlNode *
519 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
520  const char *interval_spec, const char *timeout)
521 {
522  xmlNode *xml_op;
523 
524  CRM_CHECK(prefix && task && interval_spec, return NULL);
525 
526  xml_op = pcmk__xe_create(parent, PCMK_XE_OP);
527  pcmk__xe_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
528  crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec);
529  crm_xml_add(xml_op, PCMK_XA_NAME, task);
530  if (timeout) {
532  }
533  return xml_op;
534 }
535 
545 bool
546 crm_op_needs_metadata(const char *rsc_class, const char *op)
547 {
548  /* Agent metadata is used to determine whether an agent reload is possible,
549  * so if this op is not relevant to that feature, we don't need metadata.
550  */
551 
552  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
553 
554  if ((rsc_class != NULL)
556  // Metadata is needed only for resource classes that use parameters
557  return false;
558  }
559  if (op == NULL) {
560  return true;
561  }
562 
563  // Metadata is needed only for these actions
568  PCMK_ACTION_NOTIFY, NULL);
569 }
570 
580 bool
582 {
584 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
pcmk__action_type
A dumping ground.
No connection to executor.
Definition: results.h:317
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:195
#define PCMK_XA_NAME
Definition: xml_names.h:330
const char * user_data
Definition: lrmd_events.h:47
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
#define PCMK_ACTION_MONITOR
Definition: actions.h:51
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:50
#define PCMK_ACTION_DO_SHUTDOWN
Definition: actions.h:42
Resource agent executor.
enum pcmk__action_type pcmk__parse_action(const char *action_name)
Definition: actions.c:90
Necessary CIB secrets are unavailable.
Definition: results.h:320
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: actions.c:402
#define PCMK_ACTION_RELOAD
Definition: actions.h:60
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: actions.c:482
enum ocf_exitcode rc
Definition: lrmd_events.h:65
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:519
Action did not complete in time.
Definition: results.h:311
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:609
Action was cancelled.
Definition: results.h:310
No fence device is configured for target.
Definition: results.h:319
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml_element.c:407
const char * action
Definition: pcmk_fence.c:32
Scheduler API.
pcmk__on_fail
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
#define PCMK_ACTION_RELOAD_AGENT
Definition: actions.h:61
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
#define PCMK_ACTION_REBOOT
Definition: actions.h:59
#define crm_warn(fmt, args...)
Definition: logging.h:362
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: actions.c:335
const char * pcmk__action_text(enum pcmk__action_type action)
Definition: actions.c:34
Utility functions.
const char * pcmk__on_fail_text(enum pcmk__on_fail on_fail)
Definition: actions.c:146
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
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:424
#define PCMK_ACTION_START
Definition: actions.h:63
Wrappers for and extensions to libxml2.
int rsc_op_expected_rc(const lrmd_event_data_t *op)
Definition: actions.c:471
#define PCMK_ACTION_STOP
Definition: actions.h:66
#define PCMK_ACTION_STONITH
Definition: actions.h:65
#define PCMK_ACTION_NOTIFIED
Definition: actions.h:52
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:248
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition: actions.c:546
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1051
#define pcmk__str_copy(str)
#define PCMK_META_TIMEOUT
Definition: options.h:114
#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:312
pcmk__action_result_t result
Definition: pcmk_fence.c:37
#define crm_err(fmt, args...)
Definition: logging.h:359
#define PCMK_ACTION_STOPPED
Definition: actions.h:67
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:49
#define pcmk__mem_assert(ptr)
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
void pcmk__xe_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
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:361
Action is in progress.
Definition: results.h:308
#define PCMK_ACTION_PROMOTED
Definition: actions.h:58
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:27
#define PCMK_ACTION_OFF
Definition: actions.h:54
#define PCMK_ACTION_RUNNING
Definition: actions.h:62
const char * parent
Definition: cib.c:27
Action cannot be attempted (e.g. shutdown)
Definition: results.h:318
#define PCMK_ACTION_DEMOTED
Definition: actions.h:41
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:257
bool pcmk__is_fencing_action(const char *action)
Definition: actions.c:581
unsigned int timeout
Definition: pcmk_fence.c:34
Execution failed, may be retried.
Definition: results.h:313
#define PCMK_ACTION_NOTIFY
Definition: actions.h:53
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1