pacemaker  2.1.1-52dc28db4
Scalable High-Availability cluster resource manager
operations.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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 SSCANF_HAS_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_strerror(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) {
441  case PCMK_LRM_OP_PENDING:
442  return FALSE;
443 
445  case PCMK_LRM_OP_TIMEOUT:
446  case PCMK_LRM_OP_ERROR:
448  case PCMK_LRM_OP_INVALID:
449  return TRUE;
450 
451  default:
452  if (target_rc != op->rc) {
453  return TRUE;
454  }
455  }
456 
457  return FALSE;
458 }
459 
471 xmlNode *
472 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
473  const char *interval_spec, const char *timeout)
474 {
475  xmlNode *xml_op;
476 
477  CRM_CHECK(prefix && task && interval_spec, return NULL);
478 
479  xml_op = create_xml_node(parent, XML_ATTR_OP);
480  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
481  crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
482  crm_xml_add(xml_op, "name", task);
483  if (timeout) {
485  }
486  return xml_op;
487 }
488 
498 bool
499 crm_op_needs_metadata(const char *rsc_class, const char *op)
500 {
501  /* Agent meta-data is used to determine whether an agent reload is possible,
502  * and to evaluate versioned parameters -- so if this op is not relevant to
503  * those features, we don't need the meta-data.
504  */
505 
506  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
507 
508  if ((rsc_class != NULL)
510  /* Meta-data is only needed for resource classes that use parameters */
511  return false;
512  }
513  if (op == NULL) {
514  return true;
515  }
516 
517  /* Meta-data is only needed for these actions */
522  CRMD_ACTION_NOTIFY, NULL);
523 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:218
A dumping ground.
#define CRMD_ACTION_MIGRATED
Definition: crm.h:174
const char * pcmk_strerror(int rc)
Definition: results.c:58
const char * user_data
Definition: lrmd.h:207
gboolean did_rsc_op_fail(lrmd_event_data_t *op, int target_rc)
Definition: operations.c:437
#define CRMD_ACTION_NOTIFY
Definition: crm.h:187
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:291
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:324
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:310
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:123
#define CRMD_ACTION_PROMOTE
Definition: crm.h:182
Resource agent executor.
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:472
enum ocf_exitcode rc
Definition: lrmd.h:221
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
char * strndup(const char *str, size_t len)
char * crm_meta_name(const char *field)
Definition: utils.c:511
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:176
#define crm_warn(fmt, args...)
Definition: logging.h:351
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:172
op_status
Definition: services.h:86
#define CRMD_ACTION_DEMOTE
Definition: crm.h:184
#define XML_ATTR_OP
Definition: msg_xml.h:134
int rc
Definition: pcmk_fence.c:35
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:623
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:727
Utility functions.
#define XML_ATTR_ID
Definition: msg_xml.h:129
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
Definition: xml.c:630
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:114
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:696
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:390
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:979
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:499
#define CRMD_ACTION_RELOAD
Definition: crm.h:171
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:300
int rsc_op_expected_rc(lrmd_event_data_t *op)
Definition: operations.c:426
#define CRM_META
Definition: crm.h:78
#define crm_err(fmt, args...)
Definition: logging.h:350
#define CRM_ASSERT(expr)
Definition: results.h:42
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:295
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:173
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:112
#define pcmk_ok
Definition: results.h:67
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:299
#define PCMK__OP_FMT
Definition: internal.h:168
unsigned int timeout
Definition: pcmk_fence.c:32
#define CRMD_ACTION_STATUS
Definition: crm.h:190