pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
operations.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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 <regex.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <ctype.h>
22 
23 #include <crm/crm.h>
24 #include <crm/lrmd.h>
25 #include <crm/msg_xml.h>
26 #include <crm/common/xml.h>
28 #include <crm/common/util.h>
29 
30 static regex_t *notify_migrate_re = NULL;
31 
44 char *
45 pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
46 {
47  CRM_ASSERT(rsc_id != NULL);
48  CRM_ASSERT(op_type != NULL);
49  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
50 }
51 
52 static inline gboolean
53 convert_interval(const char *s, guint *interval_ms)
54 {
55  unsigned long l;
56 
57  errno = 0;
58  l = strtoul(s, NULL, 10);
59 
60  if (errno != 0) {
61  return FALSE;
62  }
63 
64  *interval_ms = (guint) l;
65  return TRUE;
66 }
67 
68 static gboolean
69 try_fast_match(const char *key, const char *underbar1, const char *underbar2,
70  char **rsc_id, char **op_type, guint *interval_ms)
71 {
72  if (interval_ms) {
73  if (!convert_interval(underbar2+1, interval_ms)) {
74  return FALSE;
75  }
76  }
77 
78  if (rsc_id) {
79  *rsc_id = strndup(key, underbar1-key);
80  }
81 
82  if (op_type) {
83  *op_type = strndup(underbar1+1, underbar2-underbar1-1);
84  }
85 
86  return TRUE;
87 }
88 
89 static gboolean
90 try_basic_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
91 {
92  char *interval_sep = NULL;
93  char *type_sep = NULL;
94 
95  // Parse interval at end of string
96  interval_sep = strrchr(key, '_');
97  if (interval_sep == NULL) {
98  return FALSE;
99  }
100 
101  if (interval_ms) {
102  if (!convert_interval(interval_sep+1, interval_ms)) {
103  return FALSE;
104  }
105  }
106 
107  type_sep = interval_sep-1;
108 
109  while (1) {
110  if (*type_sep == '_') {
111  break;
112  } else if (type_sep == key) {
113  if (interval_ms) {
114  *interval_ms = 0;
115  }
116 
117  return FALSE;
118  }
119 
120  type_sep--;
121  }
122 
123  if (op_type) {
124  // Add one here to skip the leading underscore we landed on in the
125  // while loop.
126  *op_type = strndup(type_sep+1, interval_sep-type_sep-1);
127  }
128 
129  // Everything else is the name of the resource.
130  if (rsc_id) {
131  *rsc_id = strndup(key, type_sep-key);
132  }
133 
134  return TRUE;
135 }
136 
137 static gboolean
138 try_migrate_notify_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
139 {
140  int rc = 0;
141  size_t nmatch = 8;
142  regmatch_t pmatch[nmatch];
143 
144  if (notify_migrate_re == NULL) {
145  // cppcheck-suppress memleak
146  notify_migrate_re = calloc(1, sizeof(regex_t));
147  rc = regcomp(notify_migrate_re, "^(.*)_(migrate_(from|to)|(pre|post)_notify_([a-z]+|migrate_(from|to)))_([0-9]+)$",
148  REG_EXTENDED);
149  CRM_ASSERT(rc == 0);
150  }
151 
152  rc = regexec(notify_migrate_re, key, nmatch, pmatch, 0);
153  if (rc == REG_NOMATCH) {
154  return FALSE;
155  }
156 
157  if (rsc_id) {
158  *rsc_id = strndup(key+pmatch[1].rm_so, pmatch[1].rm_eo-pmatch[1].rm_so);
159  }
160 
161  if (op_type) {
162  *op_type = strndup(key+pmatch[2].rm_so, pmatch[2].rm_eo-pmatch[2].rm_so);
163  }
164 
165  if (interval_ms) {
166  if (!convert_interval(key+pmatch[7].rm_so, interval_ms)) {
167  if (rsc_id) {
168  free(*rsc_id);
169  *rsc_id = NULL;
170  }
171 
172  if (op_type) {
173  free(*op_type);
174  *op_type = NULL;
175  }
176 
177  return FALSE;
178  }
179  }
180 
181  return TRUE;
182 }
183 
184 gboolean
185 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
186 {
187  char *underbar1 = NULL;
188  char *underbar2 = NULL;
189  char *underbar3 = NULL;
190 
191  // Initialize output variables in case of early return
192  if (rsc_id) {
193  *rsc_id = NULL;
194  }
195 
196  if (op_type) {
197  *op_type = NULL;
198  }
199 
200  if (interval_ms) {
201  *interval_ms = 0;
202  }
203 
204  CRM_CHECK(key && *key, return FALSE);
205 
206  underbar1 = strchr(key, '_');
207  if (!underbar1) {
208  return FALSE;
209  }
210 
211  underbar2 = strchr(underbar1+1, '_');
212  if (!underbar2) {
213  return FALSE;
214  }
215 
216  underbar3 = strchr(underbar2+1, '_');
217 
218  if (!underbar3) {
219  return try_fast_match(key, underbar1, underbar2,
220  rsc_id, op_type, interval_ms);
221  } else if (try_migrate_notify_match(key, rsc_id, op_type, interval_ms)) {
222  return TRUE;
223  } else {
224  return try_basic_match(key, rsc_id, op_type, interval_ms);
225  }
226 }
227 
228 char *
229 pcmk__notify_key(const char *rsc_id, const char *notify_type,
230  const char *op_type)
231 {
232  CRM_CHECK(rsc_id != NULL, return NULL);
233  CRM_CHECK(op_type != NULL, return NULL);
234  CRM_CHECK(notify_type != NULL, return NULL);
235  return crm_strdup_printf("%s_%s_notify_%s_0",
236  rsc_id, notify_type, op_type);
237 }
238 
254 gboolean
255 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
256  int *op_status, int *op_rc, int *target_rc)
257 {
258  int res = 0;
259  char *key = NULL;
260  gboolean result = TRUE;
261  int local_op_status = -1;
262  int local_op_rc = -1;
263 
264  CRM_CHECK(magic != NULL, return FALSE);
265 
266 #ifdef HAVE_SSCANF_M
267  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
268 #else
269  key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
270  CRM_ASSERT(key);
271  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
272 #endif
273  if (res == EOF) {
274  crm_err("Could not decode transition information '%s': %s",
275  magic, pcmk_rc_str(errno));
276  result = FALSE;
277  } else if (res < 3) {
278  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
279  magic, res);
280  result = FALSE;
281  } else {
282  if (op_status) {
283  *op_status = local_op_status;
284  }
285  if (op_rc) {
286  *op_rc = local_op_rc;
287  }
288  result = decode_transition_key(key, uuid, transition_id, action_id,
289  target_rc);
290  }
291  free(key);
292  return result;
293 }
294 
295 char *
296 pcmk__transition_key(int transition_id, int action_id, int target_rc,
297  const char *node)
298 {
299  CRM_CHECK(node != NULL, return NULL);
300  return crm_strdup_printf("%d:%d:%d:%-*s",
301  action_id, transition_id, target_rc, 36, node);
302 }
303 
317 gboolean
318 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
319  int *target_rc)
320 {
321  int local_transition_id = -1;
322  int local_action_id = -1;
323  int local_target_rc = -1;
324  char local_uuid[37] = { '\0' };
325 
326  // Initialize any supplied output arguments
327  if (uuid) {
328  *uuid = NULL;
329  }
330  if (transition_id) {
331  *transition_id = -1;
332  }
333  if (action_id) {
334  *action_id = -1;
335  }
336  if (target_rc) {
337  *target_rc = -1;
338  }
339 
340  CRM_CHECK(key != NULL, return FALSE);
341  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
342  &local_target_rc, local_uuid) != 4) {
343  crm_err("Invalid transition key '%s'", key);
344  return FALSE;
345  }
346  if (strlen(local_uuid) != 36) {
347  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
348  }
349  if (uuid) {
350  *uuid = strdup(local_uuid);
351  CRM_ASSERT(*uuid);
352  }
353  if (transition_id) {
354  *transition_id = local_transition_id;
355  }
356  if (action_id) {
357  *action_id = local_action_id;
358  }
359  if (target_rc) {
360  *target_rc = local_target_rc;
361  }
362  return TRUE;
363 }
364 
365 // Return true if a is an attribute that should be filtered
366 static bool
367 should_filter_for_digest(xmlAttrPtr a, void *user_data)
368 {
369  if (strncmp((const char *) a->name, CRM_META "_",
370  sizeof(CRM_META " ") - 1) == 0) {
371  return true;
372  }
373  return pcmk__str_any_of((const char *) a->name,
374  XML_ATTR_ID,
379  "pcmk_external_ip",
380  NULL);
381 }
382 
389 void
390 pcmk__filter_op_for_digest(xmlNode *param_set)
391 {
392  char *key = NULL;
393  char *timeout = NULL;
394  guint interval_ms = 0;
395 
396  if (param_set == NULL) {
397  return;
398  }
399 
400  /* Timeout is useful for recurring operation digests, so grab it before
401  * removing meta-attributes
402  */
404  if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
405  interval_ms = 0;
406  }
407  free(key);
408  key = NULL;
409  if (interval_ms != 0) {
411  timeout = crm_element_value_copy(param_set, key);
412  }
413 
414  // Remove all CRM_meta_* attributes and certain other attributes
415  pcmk__xe_remove_matching_attrs(param_set, should_filter_for_digest, NULL);
416 
417  // Add timeout back for recurring operation digests
418  if (timeout != NULL) {
419  crm_xml_add(param_set, key, timeout);
420  }
421  free(timeout);
422  free(key);
423 }
424 
425 int
427 {
428  int rc = 0;
429 
430  if (op && op->user_data) {
431  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
432  }
433  return rc;
434 }
435 
436 gboolean
437 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
438 {
439  switch (op->op_status) {
440  case PCMK_EXEC_CANCELLED:
441  case PCMK_EXEC_PENDING:
442  return FALSE;
443 
445  case PCMK_EXEC_TIMEOUT:
446  case PCMK_EXEC_ERROR:
450  case PCMK_EXEC_INVALID:
451  return TRUE;
452 
453  default:
454  if (target_rc != op->rc) {
455  return TRUE;
456  }
457  }
458 
459  return FALSE;
460 }
461 
473 xmlNode *
474 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
475  const char *interval_spec, const char *timeout)
476 {
477  xmlNode *xml_op;
478 
479  CRM_CHECK(prefix && task && interval_spec, return NULL);
480 
482  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
483  crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
484  crm_xml_add(xml_op, "name", task);
485  if (timeout) {
487  }
488  return xml_op;
489 }
490 
500 bool
501 crm_op_needs_metadata(const char *rsc_class, const char *op)
502 {
503  /* Agent metadata is used to determine whether an agent reload is possible,
504  * so if this op is not relevant to that feature, we don't need metadata.
505  */
506 
507  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
508 
509  if ((rsc_class != NULL)
511  // Metadata is needed only for resource classes that use parameters
512  return false;
513  }
514  if (op == NULL) {
515  return true;
516  }
517 
518  // Metadata is needed only for these actions
523  CRMD_ACTION_NOTIFY, NULL);
524 }
525 
534 bool
536 {
537  return pcmk__str_any_of(action, "off", "reboot", "poweroff", NULL);
538 }
539 
540 bool
541 pcmk_is_probe(const char *task, guint interval)
542 {
543  if (task == NULL) {
544  return false;
545  }
546 
547  return (interval == 0) && pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_none);
548 }
549 
550 bool
551 pcmk_xe_is_probe(xmlNode *xml_op)
552 {
553  const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
554  const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS);
555  int interval_ms;
556 
557  pcmk__scan_min_int(interval_ms_s, &interval_ms, 0);
558  return pcmk_is_probe(task, interval_ms);
559 }
560 
561 bool
563 {
564  int status = PCMK_EXEC_UNKNOWN;
565  int rc = PCMK_OCF_OK;
566 
567  if (!pcmk_xe_is_probe(xml_op)) {
568  return false;
569  }
570 
573 
574  return rc == PCMK_OCF_NOT_INSTALLED || rc == PCMK_OCF_INVALID_PARAM ||
575  status == PCMK_EXEC_NOT_INSTALLED;
576 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
A dumping ground.
#define CRMD_ACTION_MIGRATED
Definition: crm.h:172
No connection to executor.
Definition: results.h:320
const char * user_data
Definition: lrmd.h:225
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: operations.c:437
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
bool pcmk__is_fencing_action(const char *action)
Definition: operations.c:535
#define CRMD_ACTION_NOTIFY
Definition: crm.h:185
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:294
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:323
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:313
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:128
#define CRMD_ACTION_PROMOTE
Definition: crm.h:180
Resource agent executor.
Necessary CIB secrets are unavailable.
Definition: results.h:323
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: operations.c:474
enum ocf_exitcode rc
Definition: lrmd.h:239
Action did not complete in time.
Definition: results.h:314
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
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: operations.c:255
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:553
Action was cancelled.
Definition: results.h:313
char * strndup(const char *str, size_t len)
No fence device is configured for target.
Definition: results.h:322
char * crm_meta_name(const char *field)
Definition: utils.c:468
const char * action
Definition: pcmk_fence.c:30
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: operations.c:296
#define CRMD_ACTION_START
Definition: crm.h:174
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:300
#define crm_warn(fmt, args...)
Definition: logging.h:360
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:45
#define CRMD_ACTION_RELOAD_AGENT
Definition: crm.h:170
#define CRMD_ACTION_DEMOTE
Definition: crm.h:182
op_status
#define XML_ATTR_OP
Definition: msg_xml.h:140
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:610
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:714
Utility functions.
Used only to initialize variables.
Definition: results.h:310
#define XML_ATTR_ID
Definition: msg_xml.h:134
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
Parameter invalid (in local context)
Definition: results.h:166
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:682
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:121
void crm_xml_set_id(xmlNode *xml, const char *format,...) G_GNUC_PRINTF(2
Wrappers for and extensions to libxml2.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:390
Dependencies not available locally.
Definition: results.h:169
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:952
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: operations.c:318
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition: operations.c:501
#define CRMD_ACTION_RELOAD
Definition: crm.h:169
bool pcmk_xe_is_probe(xmlNode *xml_op)
Definition: operations.c:551
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:303
int rsc_op_expected_rc(lrmd_event_data_t *op)
Definition: operations.c:426
bool pcmk_xe_mask_probe_failure(xmlNode *xml_op)
Definition: operations.c:562
Agent does not implement requested action.
Definition: results.h:315
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define CRM_META
Definition: crm.h:78
#define crm_err(fmt, args...)
Definition: logging.h:359
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:164
bool pcmk_is_probe(const char *task, guint interval)
Definition: operations.c:541
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:298
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:185
#define CRMD_ACTION_MIGRATE
Definition: crm.h:171
#define XML_LRM_ATTR_OPSTATUS
Definition: msg_xml.h:310
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:118
Agent or dependency not available locally.
Definition: results.h:319
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:311
#define pcmk_ok
Definition: results.h:68
Action is in progress.
Definition: results.h:311
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:229
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:302
const char * parent
Definition: cib.c:25
Action cannot be attempted (e.g. shutdown)
Definition: results.h:321
#define PCMK__OP_FMT
Definition: internal.h:168
unsigned int timeout
Definition: pcmk_fence.c:32
Execution failed, may be retried.
Definition: results.h:316
#define CRMD_ACTION_STATUS
Definition: crm.h:188