pacemaker  2.0.5-ba59be712
Scalable High-Availability cluster resource manager
operations.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 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>
27 #include <crm/common/util.h>
28 
29 static regex_t *notify_migrate_re = NULL;
30 
43 char *
44 pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
45 {
46  CRM_ASSERT(rsc_id != NULL);
47  CRM_ASSERT(op_type != NULL);
48  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
49 }
50 
51 static inline gboolean
52 convert_interval(const char *s, guint *interval_ms)
53 {
54  unsigned long l;
55 
56  errno = 0;
57  l = strtoul(s, NULL, 10);
58 
59  if (errno != 0) {
60  return FALSE;
61  }
62 
63  *interval_ms = (guint) l;
64  return TRUE;
65 }
66 
67 static gboolean
68 try_fast_match(const char *key, const char *underbar1, const char *underbar2,
69  char **rsc_id, char **op_type, guint *interval_ms)
70 {
71  if (interval_ms) {
72  if (!convert_interval(underbar2+1, interval_ms)) {
73  return FALSE;
74  }
75  }
76 
77  if (rsc_id) {
78  *rsc_id = strndup(key, underbar1-key);
79  }
80 
81  if (op_type) {
82  *op_type = strndup(underbar1+1, underbar2-underbar1-1);
83  }
84 
85  return TRUE;
86 }
87 
88 static gboolean
89 try_basic_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
90 {
91  char *interval_sep = NULL;
92  char *type_sep = NULL;
93 
94  // Parse interval at end of string
95  interval_sep = strrchr(key, '_');
96  if (interval_sep == NULL) {
97  return FALSE;
98  }
99 
100  if (interval_ms) {
101  if (!convert_interval(interval_sep+1, interval_ms)) {
102  return FALSE;
103  }
104  }
105 
106  type_sep = interval_sep-1;
107 
108  while (1) {
109  if (*type_sep == '_') {
110  break;
111  } else if (type_sep == key) {
112  if (interval_ms) {
113  *interval_ms = 0;
114  }
115 
116  return FALSE;
117  }
118 
119  type_sep--;
120  }
121 
122  if (op_type) {
123  // Add one here to skip the leading underscore we landed on in the
124  // while loop.
125  *op_type = strndup(type_sep+1, interval_sep-type_sep-1);
126  }
127 
128  // Everything else is the name of the resource.
129  if (rsc_id) {
130  *rsc_id = strndup(key, type_sep-key);
131  }
132 
133  return TRUE;
134 }
135 
136 static gboolean
137 try_migrate_notify_match(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
138 {
139  int rc = 0;
140  size_t nmatch = 8;
141  regmatch_t pmatch[nmatch];
142 
143  if (notify_migrate_re == NULL) {
144  // cppcheck-suppress memleak
145  notify_migrate_re = calloc(1, sizeof(regex_t));
146  rc = regcomp(notify_migrate_re, "^(.*)_(migrate_(from|to)|(pre|post)_notify_([a-z]+|migrate_(from|to)))_([0-9]+)$",
147  REG_EXTENDED);
148  CRM_ASSERT(rc == 0);
149  }
150 
151  rc = regexec(notify_migrate_re, key, nmatch, pmatch, 0);
152  if (rc == REG_NOMATCH) {
153  return FALSE;
154  }
155 
156  if (rsc_id) {
157  *rsc_id = strndup(key+pmatch[1].rm_so, pmatch[1].rm_eo-pmatch[1].rm_so);
158  }
159 
160  if (op_type) {
161  *op_type = strndup(key+pmatch[2].rm_so, pmatch[2].rm_eo-pmatch[2].rm_so);
162  }
163 
164  if (interval_ms) {
165  if (!convert_interval(key+pmatch[7].rm_so, interval_ms)) {
166  if (rsc_id) {
167  free(*rsc_id);
168  *rsc_id = NULL;
169  }
170 
171  if (op_type) {
172  free(*op_type);
173  *op_type = NULL;
174  }
175 
176  return FALSE;
177  }
178  }
179 
180  return TRUE;
181 }
182 
183 gboolean
184 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
185 {
186  char *underbar1 = NULL;
187  char *underbar2 = NULL;
188  char *underbar3 = NULL;
189 
190  // Initialize output variables in case of early return
191  if (rsc_id) {
192  *rsc_id = NULL;
193  }
194 
195  if (op_type) {
196  *op_type = NULL;
197  }
198 
199  if (interval_ms) {
200  *interval_ms = 0;
201  }
202 
203  CRM_CHECK(key && *key, return FALSE);
204 
205  underbar1 = strchr(key, '_');
206  if (!underbar1) {
207  return FALSE;
208  }
209 
210  underbar2 = strchr(underbar1+1, '_');
211  if (!underbar2) {
212  return FALSE;
213  }
214 
215  underbar3 = strchr(underbar2+1, '_');
216 
217  if (!underbar3) {
218  return try_fast_match(key, underbar1, underbar2,
219  rsc_id, op_type, interval_ms);
220  } else if (try_migrate_notify_match(key, rsc_id, op_type, interval_ms)) {
221  return TRUE;
222  } else {
223  return try_basic_match(key, rsc_id, op_type, interval_ms);
224  }
225 }
226 
227 char *
228 pcmk__notify_key(const char *rsc_id, const char *notify_type,
229  const char *op_type)
230 {
231  CRM_CHECK(rsc_id != NULL, return NULL);
232  CRM_CHECK(op_type != NULL, return NULL);
233  CRM_CHECK(notify_type != NULL, return NULL);
234  return crm_strdup_printf("%s_%s_notify_%s_0",
235  rsc_id, notify_type, op_type);
236 }
237 
253 gboolean
254 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
255  int *op_status, int *op_rc, int *target_rc)
256 {
257  int res = 0;
258  char *key = NULL;
259  gboolean result = TRUE;
260  int local_op_status = -1;
261  int local_op_rc = -1;
262 
263  CRM_CHECK(magic != NULL, return FALSE);
264 
265 #ifdef SSCANF_HAS_M
266  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
267 #else
268  key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
269  CRM_ASSERT(key);
270  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
271 #endif
272  if (res == EOF) {
273  crm_err("Could not decode transition information '%s': %s",
274  magic, pcmk_strerror(errno));
275  result = FALSE;
276  } else if (res < 3) {
277  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
278  magic, res);
279  result = FALSE;
280  } else {
281  if (op_status) {
282  *op_status = local_op_status;
283  }
284  if (op_rc) {
285  *op_rc = local_op_rc;
286  }
287  result = decode_transition_key(key, uuid, transition_id, action_id,
288  target_rc);
289  }
290  free(key);
291  return result;
292 }
293 
294 char *
295 pcmk__transition_key(int transition_id, int action_id, int target_rc,
296  const char *node)
297 {
298  CRM_CHECK(node != NULL, return NULL);
299  return crm_strdup_printf("%d:%d:%d:%-*s",
300  action_id, transition_id, target_rc, 36, node);
301 }
302 
316 gboolean
317 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
318  int *target_rc)
319 {
320  int local_transition_id = -1;
321  int local_action_id = -1;
322  int local_target_rc = -1;
323  char local_uuid[37] = { '\0' };
324 
325  // Initialize any supplied output arguments
326  if (uuid) {
327  *uuid = NULL;
328  }
329  if (transition_id) {
330  *transition_id = -1;
331  }
332  if (action_id) {
333  *action_id = -1;
334  }
335  if (target_rc) {
336  *target_rc = -1;
337  }
338 
339  CRM_CHECK(key != NULL, return FALSE);
340  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
341  &local_target_rc, local_uuid) != 4) {
342  crm_err("Invalid transition key '%s'", key);
343  return FALSE;
344  }
345  if (strlen(local_uuid) != 36) {
346  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
347  }
348  if (uuid) {
349  *uuid = strdup(local_uuid);
350  CRM_ASSERT(*uuid);
351  }
352  if (transition_id) {
353  *transition_id = local_transition_id;
354  }
355  if (action_id) {
356  *action_id = local_action_id;
357  }
358  if (target_rc) {
359  *target_rc = local_target_rc;
360  }
361  return TRUE;
362 }
363 
370 void
371 pcmk__filter_op_for_digest(xmlNode *param_set)
372 {
373  char *key = NULL;
374  char *timeout = NULL;
375  guint interval_ms = 0;
376 
377  const char *attr_filter[] = {
378  XML_ATTR_ID,
383  "pcmk_external_ip"
384  };
385 
386  const int meta_len = strlen(CRM_META);
387 
388  if (param_set == NULL) {
389  return;
390  }
391 
392  // Remove the specific attributes listed in attr_filter
393  for (int lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
394  xml_remove_prop(param_set, attr_filter[lpc]);
395  }
396 
398  if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
399  interval_ms = 0;
400  }
401  free(key);
402 
404  timeout = crm_element_value_copy(param_set, key);
405 
406  // Remove all CRM_meta_* attributes
407  for (xmlAttrPtr xIter = param_set->properties; xIter != NULL; ) {
408  const char *prop_name = (const char *) (xIter->name);
409 
410  xIter = xIter->next;
411 
412  // @TODO Why is this case-insensitive?
413  if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
414  xml_remove_prop(param_set, prop_name);
415  }
416  }
417 
418  if ((interval_ms != 0) && (timeout != NULL)) {
419  // Add the timeout back, it's useful for recurring operation digests
420  crm_xml_add(param_set, key, timeout);
421  }
422 
423  free(timeout);
424  free(key);
425 }
426 
427 int
429 {
430  int rc = 0;
431 
432  if (op && op->user_data) {
433  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
434  }
435  return rc;
436 }
437 
438 gboolean
439 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
440 {
441  switch (op->op_status) {
443  case PCMK_LRM_OP_PENDING:
444  return FALSE;
445 
447  case PCMK_LRM_OP_TIMEOUT:
448  case PCMK_LRM_OP_ERROR:
450  case PCMK_LRM_OP_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 
481  xml_op = create_xml_node(parent, XML_ATTR_OP);
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 meta-data is used to determine whether a reload is possible, and to
504  * evaluate versioned parameters -- so if this op is not relevant to those
505  * features, we don't need the meta-data.
506  */
507 
508  CRM_CHECK((rsc_class != NULL) || (op != NULL), return false);
509 
510  if ((rsc_class != NULL)
512  /* Meta-data is only needed for resource classes that use parameters */
513  return false;
514  }
515  if (op == NULL) {
516  return true;
517  }
518 
519  /* Meta-data is only needed for these actions */
524 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:215
A dumping ground.
#define CRMD_ACTION_MIGRATED
Definition: crm.h:169
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:439
#define CRMD_ACTION_NOTIFY
Definition: crm.h:182
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:261
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:317
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:280
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:90
#define CRMD_ACTION_PROMOTE
Definition: crm.h:177
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:474
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:254
char * strndup(const char *str, size_t len)
char * crm_meta_name(const char *field)
Definition: utils.c:457
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: operations.c:295
#define CRMD_ACTION_START
Definition: crm.h:171
#define crm_warn(fmt, args...)
Definition: logging.h:348
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:44
op_status
Definition: services.h:89
#define CRMD_ACTION_DEMOTE
Definition: crm.h:179
#define XML_ATTR_OP
Definition: msg_xml.h:101
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:615
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:726
Utility functions.
#define XML_ATTR_ID
Definition: msg_xml.h:96
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:196
Wrappers for and extensions to libxml2.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:663
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:371
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:866
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:317
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:167
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:270
int rsc_op_expected_rc(lrmd_event_data_t *op)
Definition: operations.c:428
#define CRM_META
Definition: crm.h:71
#define crm_err(fmt, args...)
Definition: logging.h:347
#define CRM_ASSERT(expr)
Definition: results.h:42
void crm_xml_set_id(xmlNode *xml, const char *format,...) __attribute__((__format__(__printf__
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2019
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:265
#define DIMOF(a)
Definition: crm.h:57
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:184
#define CRMD_ACTION_MIGRATE
Definition: crm.h:168
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:79
#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:228
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:269
#define PCMK__OP_FMT
Definition: internal.h:204
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
unsigned int timeout
Definition: pcmk_fence.c:32
#define CRMD_ACTION_STATUS
Definition: crm.h:185