pacemaker  2.1.8-3980678f03
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  CRM_ASSERT(rsc_id != NULL);
199  CRM_ASSERT(op_type != NULL);
200  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
201 }
202 
203 static inline gboolean
204 convert_interval(const char *s, guint *interval_ms)
205 {
206  unsigned long l;
207 
208  errno = 0;
209  l = strtoul(s, NULL, 10);
210 
211  if (errno != 0) {
212  return FALSE;
213  }
214 
215  *interval_ms = (guint) l;
216  return TRUE;
217 }
218 
230 static size_t
231 match_before(const char *key, size_t position, const char **matches)
232 {
233  for (int i = 0; matches[i] != NULL; ++i) {
234  const size_t match_len = strlen(matches[i]);
235 
236  // Must have at least X_MATCH before position
237  if (position > (match_len + 1)) {
238  const size_t possible = position - match_len - 1;
239 
240  if ((key[possible] == '_')
241  && (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
242  return possible;
243  }
244  }
245  }
246  return 0;
247 }
248 
249 gboolean
250 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
251 {
252  guint local_interval_ms = 0;
253  const size_t key_len = (key == NULL)? 0 : strlen(key);
254 
255  // Operation keys must be formatted as RSC_ACTION_INTERVAL
256  size_t action_underbar = 0; // Index in key of underbar before ACTION
257  size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
258  size_t possible = 0;
259 
260  /* Underbar was a poor choice of separator since both RSC and ACTION can
261  * contain underbars. Here, list action names and name prefixes that can.
262  */
263  const char *actions_with_underbars[] = {
266  NULL
267  };
268  const char *action_prefixes_with_underbars[] = {
269  "pre_" PCMK_ACTION_NOTIFY,
270  "post_" PCMK_ACTION_NOTIFY,
271  "confirmed-pre_" PCMK_ACTION_NOTIFY,
272  "confirmed-post_" PCMK_ACTION_NOTIFY,
273  NULL,
274  };
275 
276  // Initialize output variables in case of early return
277  if (rsc_id) {
278  *rsc_id = NULL;
279  }
280  if (op_type) {
281  *op_type = NULL;
282  }
283  if (interval_ms) {
284  *interval_ms = 0;
285  }
286 
287  // RSC_ACTION_INTERVAL implies a minimum of 5 characters
288  if (key_len < 5) {
289  return FALSE;
290  }
291 
292  // Find, parse, and validate interval
293  interval_underbar = key_len - 2;
294  while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
295  --interval_underbar;
296  }
297  if ((interval_underbar == 2)
298  || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
299  return FALSE;
300  }
301 
302  // Find the base (OCF) action name, disregarding prefixes
303  action_underbar = match_before(key, interval_underbar,
304  actions_with_underbars);
305  if (action_underbar == 0) {
306  action_underbar = interval_underbar - 2;
307  while ((action_underbar > 0) && (key[action_underbar] != '_')) {
308  --action_underbar;
309  }
310  if (action_underbar == 0) {
311  return FALSE;
312  }
313  }
314  possible = match_before(key, action_underbar,
315  action_prefixes_with_underbars);
316  if (possible != 0) {
317  action_underbar = possible;
318  }
319 
320  // Set output variables
321  if (rsc_id != NULL) {
322  *rsc_id = strndup(key, action_underbar);
323  pcmk__mem_assert(*rsc_id);
324  }
325  if (op_type != NULL) {
326  *op_type = strndup(key + action_underbar + 1,
327  interval_underbar - action_underbar - 1);
328  pcmk__mem_assert(*op_type);
329  }
330  if (interval_ms != NULL) {
331  *interval_ms = local_interval_ms;
332  }
333  return TRUE;
334 }
335 
336 char *
337 pcmk__notify_key(const char *rsc_id, const char *notify_type,
338  const char *op_type)
339 {
340  CRM_CHECK(rsc_id != NULL, return NULL);
341  CRM_CHECK(op_type != NULL, return NULL);
342  CRM_CHECK(notify_type != NULL, return NULL);
343  return crm_strdup_printf("%s_%s_notify_%s_0",
344  rsc_id, notify_type, op_type);
345 }
346 
362 gboolean
363 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
364  int *op_status, int *op_rc, int *target_rc)
365 {
366  int res = 0;
367  char *key = NULL;
368  gboolean result = TRUE;
369  int local_op_status = -1;
370  int local_op_rc = -1;
371 
372  CRM_CHECK(magic != NULL, return FALSE);
373 
374 #ifdef HAVE_SSCANF_M
375  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
376 #else
377  // magic must have >=4 other characters
378  key = pcmk__assert_alloc(1, strlen(magic) - 3);
379  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
380 #endif
381  if (res == EOF) {
382  crm_err("Could not decode transition information '%s': %s",
383  magic, pcmk_rc_str(errno));
384  result = FALSE;
385  } else if (res < 3) {
386  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
387  magic, res);
388  result = FALSE;
389  } else {
390  if (op_status) {
391  *op_status = local_op_status;
392  }
393  if (op_rc) {
394  *op_rc = local_op_rc;
395  }
396  result = decode_transition_key(key, uuid, transition_id, action_id,
397  target_rc);
398  }
399  free(key);
400  return result;
401 }
402 
403 char *
404 pcmk__transition_key(int transition_id, int action_id, int target_rc,
405  const char *node)
406 {
407  CRM_CHECK(node != NULL, return NULL);
408  return crm_strdup_printf("%d:%d:%d:%-*s",
409  action_id, transition_id, target_rc, 36, node);
410 }
411 
425 gboolean
426 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
427  int *target_rc)
428 {
429  int local_transition_id = -1;
430  int local_action_id = -1;
431  int local_target_rc = -1;
432  char local_uuid[37] = { '\0' };
433 
434  // Initialize any supplied output arguments
435  if (uuid) {
436  *uuid = NULL;
437  }
438  if (transition_id) {
439  *transition_id = -1;
440  }
441  if (action_id) {
442  *action_id = -1;
443  }
444  if (target_rc) {
445  *target_rc = -1;
446  }
447 
448  CRM_CHECK(key != NULL, return FALSE);
449  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
450  &local_target_rc, local_uuid) != 4) {
451  crm_err("Invalid transition key '%s'", key);
452  return FALSE;
453  }
454  if (strlen(local_uuid) != 36) {
455  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
456  }
457  if (uuid) {
458  *uuid = pcmk__str_copy(local_uuid);
459  }
460  if (transition_id) {
461  *transition_id = local_transition_id;
462  }
463  if (action_id) {
464  *action_id = local_action_id;
465  }
466  if (target_rc) {
467  *target_rc = local_target_rc;
468  }
469  return TRUE;
470 }
471 
472 int
474 {
475  int rc = 0;
476 
477  if (op && op->user_data) {
478  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
479  }
480  return rc;
481 }
482 
483 gboolean
484 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
485 {
486  switch (op->op_status) {
487  case PCMK_EXEC_CANCELLED:
488  case PCMK_EXEC_PENDING:
489  return FALSE;
490 
492  case PCMK_EXEC_TIMEOUT:
493  case PCMK_EXEC_ERROR:
497  case PCMK_EXEC_INVALID:
498  return TRUE;
499 
500  default:
501  if (target_rc != op->rc) {
502  return TRUE;
503  }
504  }
505 
506  return FALSE;
507 }
508 
520 xmlNode *
521 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
522  const char *interval_spec, const char *timeout)
523 {
524  xmlNode *xml_op;
525 
526  CRM_CHECK(prefix && task && interval_spec, return NULL);
527 
528  xml_op = pcmk__xe_create(parent, PCMK_XE_OP);
529  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
530  crm_xml_add(xml_op, PCMK_META_INTERVAL, interval_spec);
531  crm_xml_add(xml_op, PCMK_XA_NAME, task);
532  if (timeout) {
534  }
535  return xml_op;
536 }
537 
547 bool
548 crm_op_needs_metadata(const char *rsc_class, const char *op)
549 {
550  /* Agent metadata is used to determine whether an agent reload is possible,
551  * so if this op is not relevant to that feature, we don't need metadata.
552  */
553 
554  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
555 
556  if ((rsc_class != NULL)
558  // Metadata is needed only for resource classes that use parameters
559  return false;
560  }
561  if (op == NULL) {
562  return true;
563  }
564 
565  // Metadata is needed only for these actions
570  PCMK_ACTION_NOTIFY, NULL);
571 }
572 
582 bool
584 {
586  PCMK__ACTION_POWEROFF, NULL);
587 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
A dumping ground.
No connection to executor.
Definition: results.h:341
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:325
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:301
#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:344
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:404
#define PCMK_ACTION_RELOAD
Definition: actions.h:69
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: actions.c:484
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:521
Action did not complete in time.
Definition: results.h:335
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
Action was cancelled.
Definition: results.h:334
No fence device is configured for target.
Definition: results.h:343
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:337
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:98
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:426
#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:473
#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:250
#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:548
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:1050
#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_XE_OP
Definition: xml_names.h:143
#define PCMK_META_INTERVAL
Definition: options.h:91
Agent does not implement requested action.
Definition: results.h:336
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define crm_err(fmt, args...)
Definition: logging.h:391
#define CRM_ASSERT(expr)
Definition: results.h:42
#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:363
Action is in progress.
Definition: results.h:332
#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:342
#define PCMK_ACTION_DEMOTED
Definition: actions.h:50
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:720
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
bool pcmk__is_fencing_action(const char *action)
Definition: actions.c:583
unsigned int timeout
Definition: pcmk_fence.c:32
Execution failed, may be retried.
Definition: results.h:337
#define PCMK_ACTION_NOTIFY
Definition: actions.h:62