pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
actions.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 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/msg_xml.h>
25 #include <crm/common/xml.h>
27 #include <crm/common/util.h>
28 
41 char *
42 pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
43 {
44  CRM_ASSERT(rsc_id != NULL);
45  CRM_ASSERT(op_type != NULL);
46  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
47 }
48 
49 static inline gboolean
50 convert_interval(const char *s, guint *interval_ms)
51 {
52  unsigned long l;
53 
54  errno = 0;
55  l = strtoul(s, NULL, 10);
56 
57  if (errno != 0) {
58  return FALSE;
59  }
60 
61  *interval_ms = (guint) l;
62  return TRUE;
63 }
64 
76 static size_t
77 match_before(const char *key, size_t position, const char **matches)
78 {
79  for (int i = 0; matches[i] != NULL; ++i) {
80  const size_t match_len = strlen(matches[i]);
81 
82  // Must have at least X_MATCH before position
83  if (position > (match_len + 1)) {
84  const size_t possible = position - match_len - 1;
85 
86  if ((key[possible] == '_')
87  && (strncmp(key + possible + 1, matches[i], match_len) == 0)) {
88  return possible;
89  }
90  }
91  }
92  return 0;
93 }
94 
95 gboolean
96 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
97 {
98  guint local_interval_ms = 0;
99  const size_t key_len = (key == NULL)? 0 : strlen(key);
100 
101  // Operation keys must be formatted as RSC_ACTION_INTERVAL
102  size_t action_underbar = 0; // Index in key of underbar before ACTION
103  size_t interval_underbar = 0; // Index in key of underbar before INTERVAL
104  size_t possible = 0;
105 
106  /* Underbar was a poor choice of separator since both RSC and ACTION can
107  * contain underbars. Here, list action names and name prefixes that can.
108  */
109  const char *actions_with_underbars[] = {
112  NULL
113  };
114  const char *action_prefixes_with_underbars[] = {
115  "pre_" PCMK_ACTION_NOTIFY,
116  "post_" PCMK_ACTION_NOTIFY,
117  "confirmed-pre_" PCMK_ACTION_NOTIFY,
118  "confirmed-post_" PCMK_ACTION_NOTIFY,
119  NULL,
120  };
121 
122  // Initialize output variables in case of early return
123  if (rsc_id) {
124  *rsc_id = NULL;
125  }
126  if (op_type) {
127  *op_type = NULL;
128  }
129  if (interval_ms) {
130  *interval_ms = 0;
131  }
132 
133  // RSC_ACTION_INTERVAL implies a minimum of 5 characters
134  if (key_len < 5) {
135  return FALSE;
136  }
137 
138  // Find, parse, and validate interval
139  interval_underbar = key_len - 2;
140  while ((interval_underbar > 2) && (key[interval_underbar] != '_')) {
141  --interval_underbar;
142  }
143  if ((interval_underbar == 2)
144  || !convert_interval(key + interval_underbar + 1, &local_interval_ms)) {
145  return FALSE;
146  }
147 
148  // Find the base (OCF) action name, disregarding prefixes
149  action_underbar = match_before(key, interval_underbar,
150  actions_with_underbars);
151  if (action_underbar == 0) {
152  action_underbar = interval_underbar - 2;
153  while ((action_underbar > 0) && (key[action_underbar] != '_')) {
154  --action_underbar;
155  }
156  if (action_underbar == 0) {
157  return FALSE;
158  }
159  }
160  possible = match_before(key, action_underbar,
161  action_prefixes_with_underbars);
162  if (possible != 0) {
163  action_underbar = possible;
164  }
165 
166  // Set output variables
167  if (rsc_id != NULL) {
168  *rsc_id = strndup(key, action_underbar);
169  CRM_ASSERT(*rsc_id != NULL);
170  }
171  if (op_type != NULL) {
172  *op_type = strndup(key + action_underbar + 1,
173  interval_underbar - action_underbar - 1);
174  CRM_ASSERT(*op_type != NULL);
175  }
176  if (interval_ms != NULL) {
177  *interval_ms = local_interval_ms;
178  }
179  return TRUE;
180 }
181 
182 char *
183 pcmk__notify_key(const char *rsc_id, const char *notify_type,
184  const char *op_type)
185 {
186  CRM_CHECK(rsc_id != NULL, return NULL);
187  CRM_CHECK(op_type != NULL, return NULL);
188  CRM_CHECK(notify_type != NULL, return NULL);
189  return crm_strdup_printf("%s_%s_notify_%s_0",
190  rsc_id, notify_type, op_type);
191 }
192 
208 gboolean
209 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
210  int *op_status, int *op_rc, int *target_rc)
211 {
212  int res = 0;
213  char *key = NULL;
214  gboolean result = TRUE;
215  int local_op_status = -1;
216  int local_op_rc = -1;
217 
218  CRM_CHECK(magic != NULL, return FALSE);
219 
220 #ifdef HAVE_SSCANF_M
221  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
222 #else
223  key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
224  CRM_ASSERT(key);
225  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
226 #endif
227  if (res == EOF) {
228  crm_err("Could not decode transition information '%s': %s",
229  magic, pcmk_rc_str(errno));
230  result = FALSE;
231  } else if (res < 3) {
232  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
233  magic, res);
234  result = FALSE;
235  } else {
236  if (op_status) {
237  *op_status = local_op_status;
238  }
239  if (op_rc) {
240  *op_rc = local_op_rc;
241  }
242  result = decode_transition_key(key, uuid, transition_id, action_id,
243  target_rc);
244  }
245  free(key);
246  return result;
247 }
248 
249 char *
250 pcmk__transition_key(int transition_id, int action_id, int target_rc,
251  const char *node)
252 {
253  CRM_CHECK(node != NULL, return NULL);
254  return crm_strdup_printf("%d:%d:%d:%-*s",
255  action_id, transition_id, target_rc, 36, node);
256 }
257 
271 gboolean
272 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
273  int *target_rc)
274 {
275  int local_transition_id = -1;
276  int local_action_id = -1;
277  int local_target_rc = -1;
278  char local_uuid[37] = { '\0' };
279 
280  // Initialize any supplied output arguments
281  if (uuid) {
282  *uuid = NULL;
283  }
284  if (transition_id) {
285  *transition_id = -1;
286  }
287  if (action_id) {
288  *action_id = -1;
289  }
290  if (target_rc) {
291  *target_rc = -1;
292  }
293 
294  CRM_CHECK(key != NULL, return FALSE);
295  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
296  &local_target_rc, local_uuid) != 4) {
297  crm_err("Invalid transition key '%s'", key);
298  return FALSE;
299  }
300  if (strlen(local_uuid) != 36) {
301  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
302  }
303  if (uuid) {
304  *uuid = strdup(local_uuid);
305  CRM_ASSERT(*uuid);
306  }
307  if (transition_id) {
308  *transition_id = local_transition_id;
309  }
310  if (action_id) {
311  *action_id = local_action_id;
312  }
313  if (target_rc) {
314  *target_rc = local_target_rc;
315  }
316  return TRUE;
317 }
318 
319 // Return true if a is an attribute that should be filtered
320 static bool
321 should_filter_for_digest(xmlAttrPtr a, void *user_data)
322 {
323  if (strncmp((const char *) a->name, CRM_META "_",
324  sizeof(CRM_META " ") - 1) == 0) {
325  return true;
326  }
327  return pcmk__str_any_of((const char *) a->name,
328  XML_ATTR_ID,
333  "pcmk_external_ip",
334  NULL);
335 }
336 
343 void
344 pcmk__filter_op_for_digest(xmlNode *param_set)
345 {
346  char *key = NULL;
347  char *timeout = NULL;
348  guint interval_ms = 0;
349 
350  if (param_set == NULL) {
351  return;
352  }
353 
354  /* Timeout is useful for recurring operation digests, so grab it before
355  * removing meta-attributes
356  */
358  if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
359  interval_ms = 0;
360  }
361  free(key);
362  key = NULL;
363  if (interval_ms != 0) {
365  timeout = crm_element_value_copy(param_set, key);
366  }
367 
368  // Remove all CRM_meta_* attributes and certain other attributes
369  pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
370 
371  // Add timeout back for recurring operation digests
372  if (timeout != NULL) {
373  crm_xml_add(param_set, key, timeout);
374  }
375  free(timeout);
376  free(key);
377 }
378 
379 int
381 {
382  int rc = 0;
383 
384  if (op && op->user_data) {
385  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
386  }
387  return rc;
388 }
389 
390 gboolean
391 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
392 {
393  switch (op->op_status) {
394  case PCMK_EXEC_CANCELLED:
395  case PCMK_EXEC_PENDING:
396  return FALSE;
397 
399  case PCMK_EXEC_TIMEOUT:
400  case PCMK_EXEC_ERROR:
404  case PCMK_EXEC_INVALID:
405  return TRUE;
406 
407  default:
408  if (target_rc != op->rc) {
409  return TRUE;
410  }
411  }
412 
413  return FALSE;
414 }
415 
427 xmlNode *
428 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
429  const char *interval_spec, const char *timeout)
430 {
431  xmlNode *xml_op;
432 
433  CRM_CHECK(prefix && task && interval_spec, return NULL);
434 
436  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
437  crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
438  crm_xml_add(xml_op, "name", task);
439  if (timeout) {
441  }
442  return xml_op;
443 }
444 
454 bool
455 crm_op_needs_metadata(const char *rsc_class, const char *op)
456 {
457  /* Agent metadata is used to determine whether an agent reload is possible,
458  * so if this op is not relevant to that feature, we don't need metadata.
459  */
460 
461  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
462 
463  if ((rsc_class != NULL)
465  // Metadata is needed only for resource classes that use parameters
466  return false;
467  }
468  if (op == NULL) {
469  return true;
470  }
471 
472  // Metadata is needed only for these actions
477  PCMK_ACTION_NOTIFY, NULL);
478 }
479 
488 bool
490 {
492  "poweroff", NULL);
493 }
494 
495 bool
496 pcmk_is_probe(const char *task, guint interval)
497 {
498  if (task == NULL) {
499  return false;
500  }
501 
502  return (interval == 0)
503  && pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none);
504 }
505 
506 bool
507 pcmk_xe_is_probe(const xmlNode *xml_op)
508 {
509  const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
510  const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS);
511  int interval_ms;
512 
513  pcmk__scan_min_int(interval_ms_s, &interval_ms, 0);
514  return pcmk_is_probe(task, interval_ms);
515 }
516 
517 bool
518 pcmk_xe_mask_probe_failure(const xmlNode *xml_op)
519 {
520  int status = PCMK_EXEC_UNKNOWN;
521  int rc = PCMK_OCF_OK;
522 
523  if (!pcmk_xe_is_probe(xml_op)) {
524  return false;
525  }
526 
529 
530  return rc == PCMK_OCF_NOT_INSTALLED || rc == PCMK_OCF_INVALID_PARAM ||
531  status == PCMK_EXEC_NOT_INSTALLED;
532 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
A dumping ground.
No connection to executor.
Definition: results.h:326
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:42
const char * user_data
Definition: lrmd_events.h:45
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
#define PCMK_ACTION_MONITOR
Definition: actions.h:59
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:300
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:302
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:319
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:150
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:58
Resource agent executor.
Necessary CIB secrets are unavailable.
Definition: results.h:329
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: actions.c:250
#define PCMK_ACTION_RELOAD
Definition: actions.h:68
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: actions.c:391
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:428
Action did not complete in time.
Definition: results.h:320
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:483
Action was cancelled.
Definition: results.h:319
No fence device is configured for target.
Definition: results.h:328
char * crm_meta_name(const char *field)
Definition: utils.c:468
const char * action
Definition: pcmk_fence.c:30
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: actions.c:344
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:306
#define PCMK_ACTION_RELOAD_AGENT
Definition: actions.h:69
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
#define PCMK_ACTION_REBOOT
Definition: actions.h:67
#define crm_warn(fmt, args...)
Definition: logging.h:382
bool pcmk_xe_is_probe(const xmlNode *xml_op)
Definition: actions.c:507
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: actions.c:183
op_status
#define XML_ATTR_OP
Definition: msg_xml.h:161
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:540
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:644
Utility functions.
Used only to initialize variables.
Definition: results.h:316
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
Parameter invalid (in local context)
Definition: results.h:172
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:593
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:99
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:272
#define PCMK_ACTION_START
Definition: actions.h:71
Wrappers for and extensions to libxml2.
int rsc_op_expected_rc(const lrmd_event_data_t *op)
Definition: actions.c:380
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:96
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition: actions.c:455
Dependencies not available locally.
Definition: results.h:175
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:957
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:309
Agent does not implement requested action.
Definition: results.h:321
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define CRM_META
Definition: crm.h:79
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:170
bool pcmk_is_probe(const char *task, guint interval)
Definition: actions.c:496
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:57
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:304
#define XML_LRM_ATTR_OPSTATUS
Definition: msg_xml.h:316
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:140
#define PCMK_ACTION_PROMOTE
Definition: actions.h:65
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:209
Agent or dependency not available locally.
Definition: results.h:325
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:317
#define pcmk_ok
Definition: results.h:68
Action is in progress.
Definition: results.h:317
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
bool pcmk_xe_mask_probe_failure(const xmlNode *xml_op)
Definition: actions.c:518
#define PCMK_ACTION_OFF
Definition: actions.h:62
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:308
const char * parent
Definition: cib.c:27
Action cannot be attempted (e.g. shutdown)
Definition: results.h:327
bool pcmk__is_fencing_action(const char *action)
Definition: actions.c:489
unsigned int timeout
Definition: pcmk_fence.c:32
Execution failed, may be retried.
Definition: results.h:322
#define PCMK_ACTION_NOTIFY
Definition: actions.h:61