pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 
21 #include <crm/crm.h>
22 #include <crm/lrmd.h>
23 #include <crm/msg_xml.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/util.h>
26 
39 char *
40 pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
41 {
42  CRM_ASSERT(rsc_id != NULL);
43  CRM_ASSERT(op_type != NULL);
44  return crm_strdup_printf(PCMK__OP_FMT, rsc_id, op_type, interval_ms);
45 }
46 
47 gboolean
48 parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
49 {
50  char *notify = NULL;
51  char *mutable_key = NULL;
52  char *mutable_key_ptr = NULL;
53  size_t len = 0, offset = 0;
54  unsigned long long ch = 0;
55  guint local_interval_ms = 0;
56 
57  // Initialize output variables in case of early return
58  if (rsc_id) {
59  *rsc_id = NULL;
60  }
61  if (op_type) {
62  *op_type = NULL;
63  }
64  if (interval_ms) {
65  *interval_ms = 0;
66  }
67 
68  CRM_CHECK(key && *key, return FALSE);
69 
70  // Parse interval at end of string
71  len = strlen(key);
72  offset = len - 1;
73  while ((offset > 0) && isdigit(key[offset])) {
74  ch = key[offset] - '0';
75  for (int digits = len - offset; digits > 1; --digits) {
76  ch = ch * 10;
77  }
78  local_interval_ms += ch;
79  offset--;
80  }
81  crm_trace("Operation key '%s' has interval %ums", key, local_interval_ms);
82  if (interval_ms) {
83  *interval_ms = local_interval_ms;
84  }
85 
86  CRM_CHECK((offset != (len - 1)) && (key[offset] == '_'), return FALSE);
87 
88  mutable_key = strndup(key, offset);
89  offset--;
90 
91  while (offset > 0 && key[offset] != '_') {
92  offset--;
93  }
94 
95  CRM_CHECK(key[offset] == '_',
96  free(mutable_key); return FALSE);
97 
98  mutable_key_ptr = mutable_key + offset + 1;
99 
100  crm_trace(" Action: %s", mutable_key_ptr);
101  if (op_type) {
102  *op_type = strdup(mutable_key_ptr);
103  }
104 
105  mutable_key[offset] = 0;
106  offset--;
107 
108  notify = strstr(mutable_key, "_post_notify");
109  if (notify && safe_str_eq(notify, "_post_notify")) {
110  notify[0] = 0;
111  }
112 
113  notify = strstr(mutable_key, "_pre_notify");
114  if (notify && safe_str_eq(notify, "_pre_notify")) {
115  notify[0] = 0;
116  }
117 
118  crm_trace(" Resource: %s", mutable_key);
119  if (rsc_id) {
120  *rsc_id = mutable_key;
121  } else {
122  free(mutable_key);
123  }
124 
125  return TRUE;
126 }
127 
128 char *
129 pcmk__notify_key(const char *rsc_id, const char *notify_type,
130  const char *op_type)
131 {
132  CRM_CHECK(rsc_id != NULL, return NULL);
133  CRM_CHECK(op_type != NULL, return NULL);
134  CRM_CHECK(notify_type != NULL, return NULL);
135  return crm_strdup_printf("%s_%s_notify_%s_0",
136  rsc_id, notify_type, op_type);
137 }
138 
154 gboolean
155 decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id,
156  int *op_status, int *op_rc, int *target_rc)
157 {
158  int res = 0;
159  char *key = NULL;
160  gboolean result = TRUE;
161  int local_op_status = -1;
162  int local_op_rc = -1;
163 
164  CRM_CHECK(magic != NULL, return FALSE);
165 
166 #ifdef SSCANF_HAS_M
167  res = sscanf(magic, "%d:%d;%ms", &local_op_status, &local_op_rc, &key);
168 #else
169  key = calloc(1, strlen(magic) - 3); // magic must have >=4 other characters
170  CRM_ASSERT(key);
171  res = sscanf(magic, "%d:%d;%s", &local_op_status, &local_op_rc, key);
172 #endif
173  if (res == EOF) {
174  crm_err("Could not decode transition information '%s': %s",
175  magic, pcmk_strerror(errno));
176  result = FALSE;
177  } else if (res < 3) {
178  crm_warn("Transition information '%s' incomplete (%d of 3 expected items)",
179  magic, res);
180  result = FALSE;
181  } else {
182  if (op_status) {
183  *op_status = local_op_status;
184  }
185  if (op_rc) {
186  *op_rc = local_op_rc;
187  }
188  result = decode_transition_key(key, uuid, transition_id, action_id,
189  target_rc);
190  }
191  free(key);
192  return result;
193 }
194 
195 char *
196 pcmk__transition_key(int transition_id, int action_id, int target_rc,
197  const char *node)
198 {
199  CRM_CHECK(node != NULL, return NULL);
200  return crm_strdup_printf("%d:%d:%d:%-*s",
201  action_id, transition_id, target_rc, 36, node);
202 }
203 
217 gboolean
218 decode_transition_key(const char *key, char **uuid, int *transition_id, int *action_id,
219  int *target_rc)
220 {
221  int local_transition_id = -1;
222  int local_action_id = -1;
223  int local_target_rc = -1;
224  char local_uuid[37] = { '\0' };
225 
226  // Initialize any supplied output arguments
227  if (uuid) {
228  *uuid = NULL;
229  }
230  if (transition_id) {
231  *transition_id = -1;
232  }
233  if (action_id) {
234  *action_id = -1;
235  }
236  if (target_rc) {
237  *target_rc = -1;
238  }
239 
240  CRM_CHECK(key != NULL, return FALSE);
241  if (sscanf(key, "%d:%d:%d:%36s", &local_action_id, &local_transition_id,
242  &local_target_rc, local_uuid) != 4) {
243  crm_err("Invalid transition key '%s'", key);
244  return FALSE;
245  }
246  if (strlen(local_uuid) != 36) {
247  crm_warn("Invalid UUID '%s' in transition key '%s'", local_uuid, key);
248  }
249  if (uuid) {
250  *uuid = strdup(local_uuid);
251  CRM_ASSERT(*uuid);
252  }
253  if (transition_id) {
254  *transition_id = local_transition_id;
255  }
256  if (action_id) {
257  *action_id = local_action_id;
258  }
259  if (target_rc) {
260  *target_rc = local_target_rc;
261  }
262  return TRUE;
263 }
264 
271 void
272 pcmk__filter_op_for_digest(xmlNode *param_set)
273 {
274  char *key = NULL;
275  char *timeout = NULL;
276  guint interval_ms = 0;
277 
278  const char *attr_filter[] = {
279  XML_ATTR_ID,
284  "pcmk_external_ip"
285  };
286 
287  const int meta_len = strlen(CRM_META);
288 
289  if (param_set == NULL) {
290  return;
291  }
292 
293  // Remove the specific attributes listed in attr_filter
294  for (int lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
295  xml_remove_prop(param_set, attr_filter[lpc]);
296  }
297 
299  if (crm_element_value_ms(param_set, key, &interval_ms) != pcmk_ok) {
300  interval_ms = 0;
301  }
302  free(key);
303 
305  timeout = crm_element_value_copy(param_set, key);
306 
307  // Remove all CRM_meta_* attributes
308  for (xmlAttrPtr xIter = param_set->properties; xIter != NULL; ) {
309  const char *prop_name = (const char *) (xIter->name);
310 
311  xIter = xIter->next;
312 
313  // @TODO Why is this case-insensitive?
314  if (strncasecmp(prop_name, CRM_META, meta_len) == 0) {
315  xml_remove_prop(param_set, prop_name);
316  }
317  }
318 
319  if ((interval_ms != 0) && (timeout != NULL)) {
320  // Add the timeout back, it's useful for recurring operation digests
321  crm_xml_add(param_set, key, timeout);
322  }
323 
324  free(timeout);
325  free(key);
326 }
327 
328 int
330 {
331  int rc = 0;
332 
333  if (op && op->user_data) {
334  decode_transition_key(op->user_data, NULL, NULL, NULL, &rc);
335  }
336  return rc;
337 }
338 
339 gboolean
340 did_rsc_op_fail(lrmd_event_data_t * op, int target_rc)
341 {
342  switch (op->op_status) {
344  case PCMK_LRM_OP_PENDING:
345  return FALSE;
346  break;
347 
349  case PCMK_LRM_OP_TIMEOUT:
350  case PCMK_LRM_OP_ERROR:
352  case PCMK_LRM_OP_INVALID:
353  return TRUE;
354  break;
355 
356  default:
357  if (target_rc != op->rc) {
358  return TRUE;
359  }
360  }
361 
362  return FALSE;
363 }
364 
376 xmlNode *
377 crm_create_op_xml(xmlNode *parent, const char *prefix, const char *task,
378  const char *interval_spec, const char *timeout)
379 {
380  xmlNode *xml_op;
381 
382  CRM_CHECK(prefix && task && interval_spec, return NULL);
383 
384  xml_op = create_xml_node(parent, XML_ATTR_OP);
385  crm_xml_set_id(xml_op, "%s-%s-%s", prefix, task, interval_spec);
386  crm_xml_add(xml_op, XML_LRM_ATTR_INTERVAL, interval_spec);
387  crm_xml_add(xml_op, "name", task);
388  if (timeout) {
389  crm_xml_add(xml_op, XML_ATTR_TIMEOUT, timeout);
390  }
391  return xml_op;
392 }
393 
403 bool
404 crm_op_needs_metadata(const char *rsc_class, const char *op)
405 {
406  /* Agent meta-data is used to determine whether a reload is possible, and to
407  * evaluate versioned parameters -- so if this op is not relevant to those
408  * features, we don't need the meta-data.
409  */
410 
411  CRM_CHECK(rsc_class || op, return FALSE);
412 
413  if (rsc_class
414  && is_not_set(pcmk_get_ra_caps(rsc_class), pcmk_ra_cap_params)) {
415  /* Meta-data is only needed for resource classes that use parameters */
416  return FALSE;
417  }
418 
419  /* Meta-data is only needed for these actions */
420  if (op
421  && strcmp(op, CRMD_ACTION_START)
422  && strcmp(op, CRMD_ACTION_STATUS)
423  && strcmp(op, CRMD_ACTION_PROMOTE)
424  && strcmp(op, CRMD_ACTION_DEMOTE)
425  && strcmp(op, CRMD_ACTION_RELOAD)
426  && strcmp(op, CRMD_ACTION_MIGRATE)
427  && strcmp(op, CRMD_ACTION_MIGRATED)
428  && strcmp(op, CRMD_ACTION_NOTIFY)) {
429  return FALSE;
430  }
431 
432  return TRUE;
433 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
A dumping ground.
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:48
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:377
#define CRMD_ACTION_MIGRATED
Definition: crm.h:169
const char * pcmk_strerror(int rc)
Definition: results.c:55
const char * user_data
Definition: lrmd.h:207
int rsc_op_expected_rc(lrmd_event_data_t *event)
Definition: operations.c:329
#define CRMD_ACTION_NOTIFY
Definition: crm.h:182
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:254
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:316
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:273
#define XML_ATTR_TIMEOUT
Definition: msg_xml.h:90
#define CRMD_ACTION_PROMOTE
Definition: crm.h:177
Resource agent executor.
bool crm_op_needs_metadata(const char *rsc_class, const char *op)
Check whether an operation requires resource agent meta-data.
Definition: operations.c:404
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:272
enum ocf_exitcode rc
Definition: lrmd.h:221
char * strndup(const char *str, size_t len)
char * crm_meta_name(const char *field)
Definition: utils.c:454
#define CRMD_ACTION_START
Definition: crm.h:171
#define crm_warn(fmt, args...)
Definition: logging.h:364
op_status
Definition: services.h:118
#define CRMD_ACTION_DEMOTE
Definition: crm.h:179
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:129
#define XML_ATTR_OP
Definition: msg_xml.h:101
int rc
Definition: pcmk_fence.c:34
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:614
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:725
Utility functions.
#define XML_ATTR_ID
Definition: msg_xml.h:96
#define crm_trace(fmt, args...)
Definition: logging.h:369
Wrappers for and extensions to libxml2.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1976
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
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:40
#define CRMD_ACTION_RELOAD
Definition: crm.h:167
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:263
gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc)
Definition: operations.c:340
#define CRM_META
Definition: crm.h:71
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
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:218
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:3340
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:258
#define DIMOF(a)
Definition: crm.h:57
#define CRMD_ACTION_MIGRATE
Definition: crm.h:168
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:79
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:155
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: operations.c:196
#define pcmk_ok
Definition: results.h:67
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:262
#define safe_str_eq(a, b)
Definition: util.h:65
#define PCMK__OP_FMT
Definition: internal.h:147
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
unsigned int timeout
Definition: pcmk_fence.c:31
#define CRMD_ACTION_STATUS
Definition: crm.h:185