pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
ipc_attrd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011-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 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13 
14 #include <crm_internal.h>
15 
16 #include <stdio.h>
17 
18 #include <crm/crm.h>
19 #include <crm/common/ipc.h>
22 #include <crm/msg_xml.h>
23 #include "crmcommon_private.h"
24 
25 static void
26 set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
27 {
28  const char *name = NULL;
30 
32 
33  for (xmlNode *node = first_named_child(msg_data, XML_CIB_TAG_NODE);
34  node != NULL; node = crm_next_same_xml(node)) {
35  pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
36 
37  CRM_ASSERT(pair != NULL);
38 
40  pair->name = name;
42  data->data.pairs = g_list_prepend(data->data.pairs, pair);
43  }
44 }
45 
46 static bool
47 reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
48 {
49  const char *command = crm_element_value(request, PCMK__XA_TASK);
50 
51  return pcmk__str_any_of(command,
58  NULL);
59 }
60 
61 static bool
62 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
63 {
64  const char *value = NULL;
65  crm_exit_t status = CRM_EX_OK;
66 
67  pcmk__attrd_api_reply_t reply_data = {
69  };
70 
71  if (pcmk__str_eq((const char *) reply->name, "ack", pcmk__str_none)) {
72  return false;
73  }
74 
75  /* Do some basic validation of the reply */
76  value = crm_element_value(reply, F_TYPE);
77  if (pcmk__str_empty(value)
78  || !pcmk__str_eq(value, T_ATTRD, pcmk__str_none)) {
79  crm_info("Unrecognizable message from attribute manager: "
80  "message type '%s' not '" T_ATTRD "'", pcmk__s(value, ""));
81  status = CRM_EX_PROTOCOL;
82  goto done;
83  }
84 
85  value = crm_element_value(reply, F_SUBTYPE);
86 
87  /* Only the query command gets a reply for now. NULL counts as query for
88  * backward compatibility with attribute managers <2.1.3 that didn't set it.
89  */
90  if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
91  if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
92  status = ENXIO; // Most likely, the attribute doesn't exist
93  goto done;
94  }
96  set_pairs_data(&reply_data, reply);
97 
98  } else {
99  crm_info("Unrecognizable message from attribute manager: "
100  "message subtype '%s' unknown", pcmk__s(value, ""));
101  status = CRM_EX_PROTOCOL;
102  goto done;
103  }
104 
105 done:
106  pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
107 
108  /* Free any reply data that was allocated */
109  if (reply_data.data.pairs) {
110  g_list_free_full(reply_data.data.pairs, free);
111  }
112 
113  return false;
114 }
115 
118 {
119  pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
120 
121  if (cmds != NULL) {
122  cmds->new_data = NULL;
123  cmds->free_data = NULL;
124  cmds->post_connect = NULL;
125  cmds->reply_expected = reply_expected;
126  cmds->dispatch = dispatch;
127  }
128  return cmds;
129 }
130 
139 static xmlNode *
140 create_attrd_op(const char *user_name)
141 {
142  xmlNode *attrd_op = create_xml_node(NULL, __func__);
143 
144  crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
145  crm_xml_add(attrd_op, F_ORIG, (crm_system_name? crm_system_name: "unknown"));
146  crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
147 
148  return attrd_op;
149 }
150 
151 static int
152 create_api(pcmk_ipc_api_t **api)
153 {
154  int rc = pcmk_new_ipc_api(api, pcmk_ipc_attrd);
155 
156  if (rc != pcmk_rc_ok) {
157  crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
158  }
159 
160  return rc;
161 }
162 
163 static void
164 destroy_api(pcmk_ipc_api_t *api)
165 {
166  pcmk_disconnect_ipc(api);
167  pcmk_free_ipc_api(api);
168  api = NULL;
169 }
170 
171 static int
172 connect_and_send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
173 {
174  int rc = pcmk_rc_ok;
175  int max = 5;
176 
177  while (max > 0) {
178  crm_info("Connecting to cluster... %d retries remaining", max);
180 
181  if (rc == pcmk_rc_ok) {
182  rc = pcmk__send_ipc_request(api, request);
183  break;
184  } else if (rc == EAGAIN || rc == EALREADY) {
185  sleep(5 - max);
186  max--;
187  } else {
188  crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
189  break;
190  }
191  }
192 
193  return rc;
194 }
195 
196 static int
197 send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
198 {
199  return pcmk__send_ipc_request(api, request);
200 }
201 
202 int
204  const char *resource, const char *operation,
205  const char *interval_spec, const char *user_name,
206  uint32_t options)
207 {
208  int rc = pcmk_rc_ok;
209  xmlNode *request = create_attrd_op(user_name);
210  const char *interval_desc = NULL;
211  const char *op_desc = NULL;
212  const char *target = pcmk__node_attr_target(node);
213 
214  if (target != NULL) {
215  node = target;
216  }
217 
219  pcmk__xe_add_node(request, node, 0);
220  crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
221  crm_xml_add(request, PCMK__XA_ATTR_OPERATION, operation);
222  crm_xml_add(request, PCMK__XA_ATTR_INTERVAL, interval_spec);
225 
226  if (api == NULL) {
227  rc = create_api(&api);
228  if (rc != pcmk_rc_ok) {
229  return rc;
230  }
231 
232  rc = connect_and_send_attrd_request(api, request);
233  destroy_api(api);
234 
235  } else if (!pcmk_ipc_is_connected(api)) {
236  rc = connect_and_send_attrd_request(api, request);
237 
238  } else {
239  rc = send_attrd_request(api, request);
240  }
241 
242  free_xml(request);
243 
244  if (operation) {
245  interval_desc = interval_spec? interval_spec : "nonrecurring";
246  op_desc = operation;
247  } else {
248  interval_desc = "all";
249  op_desc = "operations";
250  }
251 
252  crm_debug("Asked pacemaker-attrd to clear failure of %s %s for %s on %s: %s (%d)",
253  interval_desc, op_desc, (resource? resource : "all resources"),
254  (node? node : "all nodes"), pcmk_rc_str(rc), rc);
255 
256  return rc;
257 }
258 
259 int
260 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
261  uint32_t options)
262 {
263  const char *target = NULL;
264 
265  if (name == NULL) {
266  return EINVAL;
267  }
268 
270 
271  if (target != NULL) {
272  node = target;
273  }
274 
275  /* Make sure the right update option is set. */
276  options &= ~pcmk__node_attr_delay;
277  options |= pcmk__node_attr_value;
278 
279  return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
280 }
281 
282 int
283 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
284 {
285  int rc = pcmk_rc_ok;
286  xmlNode *request = NULL;
287  const char *display_host = (node ? node : "localhost");
288  const char *target = pcmk__node_attr_target(node);
289 
290  if (target != NULL) {
291  node = target;
292  }
293 
294  request = create_attrd_op(NULL);
295 
297  pcmk__xe_add_node(request, node, 0);
298 
299  if (api == NULL) {
300  rc = create_api(&api);
301  if (rc != pcmk_rc_ok) {
302  return rc;
303  }
304 
305  rc = connect_and_send_attrd_request(api, request);
306  destroy_api(api);
307 
308  } else if (!pcmk_ipc_is_connected(api)) {
309  rc = connect_and_send_attrd_request(api, request);
310 
311  } else {
312  rc = send_attrd_request(api, request);
313  }
314 
315  free_xml(request);
316 
317  crm_debug("Asked pacemaker-attrd to purge %s: %s (%d)",
318  display_host, pcmk_rc_str(rc), rc);
319 
320  return rc;
321 }
322 
323 int
324 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
325  uint32_t options)
326 {
327  int rc = pcmk_rc_ok;
328  xmlNode *request = NULL;
329  const char *target = NULL;
330 
331  if (name == NULL) {
332  return EINVAL;
333  }
334 
336 
337  if (target != NULL) {
338  node = target;
339  }
340 
341  request = create_attrd_op(NULL);
342 
345  pcmk__xe_add_node(request, node, 0);
346 
347  rc = send_attrd_request(api, request);
348  free_xml(request);
349 
350  if (node) {
351  crm_debug("Queried pacemaker-attrd for %s on %s: %s (%d)",
352  name, node, pcmk_rc_str(rc), rc);
353  } else {
354  crm_debug("Queried pacemaker-attrd for %s: %s (%d)",
355  name, pcmk_rc_str(rc), rc);
356  }
357 
358  return rc;
359 }
360 
361 int
363 {
364  int rc = pcmk_rc_ok;
365  xmlNode *request = NULL;
366  const char *display_host = (node ? node : "localhost");
367  const char *target = pcmk__node_attr_target(node);
368 
369  if (target != NULL) {
370  node = target;
371  }
372 
373  request = create_attrd_op(NULL);
374 
376  pcmk__xe_add_node(request, node, 0);
377 
378  if (api == NULL) {
379  rc = create_api(&api);
380  if (rc != pcmk_rc_ok) {
381  return rc;
382  }
383 
384  rc = connect_and_send_attrd_request(api, request);
385  destroy_api(api);
386 
387  } else if (!pcmk_ipc_is_connected(api)) {
388  rc = connect_and_send_attrd_request(api, request);
389 
390  } else {
391  rc = send_attrd_request(api, request);
392  }
393 
394  free_xml(request);
395 
396  crm_debug("Asked pacemaker-attrd to refresh %s: %s (%d)",
397  display_host, pcmk_rc_str(rc), rc);
398 
399  return rc;
400 }
401 
402 static void
403 add_op_attr(xmlNode *op, uint32_t options)
404 {
405  if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
407  } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
409  } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
411  }
412 }
413 
414 static void
415 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
416  const char *dampen, const char *set, uint32_t options)
417 {
418  if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
420  } else {
422  }
423 
424  add_op_attr(op, options);
425 
426  crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
428  pcmk__xe_add_node(op, node, 0);
429  crm_xml_add(op, PCMK__XA_ATTR_SET, set);
434 }
435 
436 int
437 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
438  const char *value, const char *dampen, const char *set,
439  const char *user_name, uint32_t options)
440 {
441  int rc = pcmk_rc_ok;
442  xmlNode *request = NULL;
443  const char *display_host = (node ? node : "localhost");
444  const char *target = NULL;
445 
446  if (name == NULL) {
447  return EINVAL;
448  }
449 
451 
452  if (target != NULL) {
453  node = target;
454  }
455 
456  request = create_attrd_op(user_name);
457  populate_update_op(request, node, name, value, dampen, set, options);
458 
459  if (api == NULL) {
460  rc = create_api(&api);
461  if (rc != pcmk_rc_ok) {
462  return rc;
463  }
464 
465  rc = connect_and_send_attrd_request(api, request);
466  destroy_api(api);
467 
468  } else if (!pcmk_ipc_is_connected(api)) {
469  rc = connect_and_send_attrd_request(api, request);
470 
471  } else {
472  rc = send_attrd_request(api, request);
473  }
474 
475  free_xml(request);
476 
477  crm_debug("Asked pacemaker-attrd to update %s on %s: %s (%d)",
478  name, display_host, pcmk_rc_str(rc), rc);
479 
480  return rc;
481 }
482 
483 int
484 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
485  const char *set, const char *user_name,
486  uint32_t options)
487 {
488  int rc = pcmk_rc_ok;
489  xmlNode *request = NULL;
490 
491  if (attrs == NULL) {
492  return EINVAL;
493  }
494 
495  /* There are two different ways of handling a list of attributes:
496  *
497  * (1) For messages originating from some command line tool, we have to send
498  * them one at a time. In this loop, we just call pcmk__attrd_api_update
499  * for each, letting it deal with creating the API object if it doesn't
500  * already exist.
501  *
502  * The reason we can't use a single message in this case is that we can't
503  * trust that the server supports it. Remote nodes could be involved
504  * here, and there's no guarantee that a newer client running on a remote
505  * node is talking to (or proxied through) a cluster node with a newer
506  * attrd. We also can't just try sending a single message and then falling
507  * back on multiple. There's no handshake with the attrd server to
508  * determine its version. And then we would need to do that fallback in the
509  * dispatch function for this to work for all connection types (mainloop in
510  * particular), and at that point we won't know what the original message
511  * was in order to break it apart and resend as individual messages.
512  *
513  * (2) For messages between daemons, we can be assured that the local attrd
514  * will support the new message and that it can send to the other attrds
515  * as one request or split up according to the minimum supported version.
516  */
517  for (GList *iter = attrs; iter != NULL; iter = iter->next) {
518  pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
519 
520  if (pcmk__is_daemon) {
521  const char *target = NULL;
522  xmlNode *child = NULL;
523 
524  /* First time through this loop - create the basic request. */
525  if (request == NULL) {
526  request = create_attrd_op(user_name);
527  add_op_attr(request, options);
528  }
529 
530  /* Add a child node for this operation. We add the task to the top
531  * level XML node so attrd_ipc_dispatch doesn't need changes. And
532  * then we also add the task to each child node in populate_update_op
533  * so attrd_client_update knows what form of update is taking place.
534  */
535  child = create_xml_node(request, XML_ATTR_OP);
537 
538  if (target != NULL) {
539  pair->node = target;
540  }
541 
542  populate_update_op(child, pair->node, pair->name, pair->value, dampen,
543  set, options);
544  } else {
545  rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
546  dampen, set, user_name, options);
547  }
548  }
549 
550  /* If we were doing multiple attributes at once, we still need to send the
551  * request. Do that now, creating and destroying the API object if needed.
552  */
553  if (pcmk__is_daemon) {
554  bool created_api = false;
555 
556  if (api == NULL) {
557  rc = create_api(&api);
558  if (rc != pcmk_rc_ok) {
559  return rc;
560  }
561 
562  created_api = true;
563  }
564 
565  rc = connect_and_send_attrd_request(api, request);
566  free_xml(request);
567 
568  if (created_api) {
569  destroy_api(api);
570  }
571  }
572 
573  return rc;
574 }
#define T_ATTRD
Definition: msg_xml.h:85
#define PCMK__XA_ATTR_DAMPENING
Definition: crm_internal.h:61
#define PCMK__ATTRD_CMD_UPDATE_DELAY
Definition: crm_internal.h:102
int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:324
#define PCMK__XA_ATTR_NAME
Definition: crm_internal.h:66
A dumping ground.
#define F_TYPE
Definition: msg_xml.h:69
#define PCMK__ATTRD_CMD_UPDATE_BOTH
Definition: crm_internal.h:101
const char * pcmk__node_attr_target(const char *name)
Definition: attrs.c:39
char data[0]
Definition: cpg.c:55
#define PCMK__ATTRD_CMD_PEER_REMOVE
Definition: crm_internal.h:99
int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
Connect to a Pacemaker daemon via IPC.
Definition: ipc_client.c:486
const char * name
Definition: cib.c:24
int pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen, const char *set, const char *user_name, uint32_t options)
Definition: ipc_attrd.c:484
#define PCMK__XA_TASK
Definition: crm_internal.h:84
int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:362
int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server)
Create a new object for using Pacemaker daemon IPC.
Definition: ipc_client.c:47
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
#define F_SUBTYPE
Definition: msg_xml.h:65
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:419
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 PCMK__XA_ATTR_VALUE
Definition: crm_internal.h:76
char * crm_system_name
Definition: utils.c:51
enum crm_exit_e crm_exit_t
#define PCMK__XA_ATTR_RESOURCE
Definition: crm_internal.h:71
#define PCMK__ATTRD_CMD_CLEAR_FAILURE
Definition: crm_internal.h:108
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:476
G_GNUC_INTERNAL void pcmk__call_ipc_callback(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
Definition: ipc_client.c:146
int pcmk__attrd_api_clear_failures(pcmk_ipc_api_t *api, const char *node, const char *resource, const char *operation, const char *interval_spec, const char *user_name, uint32_t options)
Definition: ipc_attrd.c:203
int pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name, const char *value, const char *dampen, const char *set, const char *user_name, uint32_t options)
Definition: ipc_attrd.c:437
#define XML_ATTR_OP
Definition: msg_xml.h:140
Protocol violated.
Definition: results.h:260
union pcmk__attrd_api_reply_t::@0 data
#define crm_debug(fmt, args...)
Definition: logging.h:364
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
#define PCMK__XA_ATTR_OPERATION
Definition: crm_internal.h:69
#define PCMK__ATTRD_CMD_REFRESH
Definition: crm_internal.h:104
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
bool pcmk__is_daemon
Definition: logging.c:47
#define PCMK__XA_ATTR_INTERVAL
Definition: crm_internal.h:63
#define PCMK__XA_ATTR_IS_PRIVATE
Definition: crm_internal.h:64
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749
#define PCMK__XA_ATTR_USER
Definition: crm_internal.h:74
G_GNUC_INTERNAL int pcmk__send_ipc_request(pcmk_ipc_api_t *api, xmlNode *request)
Definition: ipc_client.c:619
Success.
Definition: results.h:234
#define PCMK__ATTRD_CMD_QUERY
Definition: crm_internal.h:103
#define PCMK__ATTRD_CMD_UPDATE
Definition: crm_internal.h:100
#define F_ORIG
Definition: msg_xml.h:57
void free_xml(xmlNode *child)
Definition: xml.c:885
int(* post_connect)(pcmk_ipc_api_t *api)
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:952
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:205
enum pcmk__attrd_api_reply reply_type
int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:283
const xmlChar * pcmkXmlStr
Definition: xml.h:50
const char * target
Definition: pcmk_fence.c:29
int(* new_data)(pcmk_ipc_api_t *api)
int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:260
#define PCMK__XA_ATTR_NODE_NAME
Definition: crm_internal.h:68
bool(* dispatch)(pcmk_ipc_api_t *api, xmlNode *msg)
#define crm_err(fmt, args...)
Definition: logging.h:359
#define CRM_ASSERT(expr)
Definition: results.h:42
Sending a command will wait for any reply.
Definition: ipc.h:91
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:202
void(* free_data)(void *api_data)
#define PCMK__XA_ATTR_IS_REMOTE
Definition: crm_internal.h:65
bool(* reply_expected)(pcmk_ipc_api_t *api, xmlNode *request)
IPC interface to Pacemaker daemons.
pcmk__ipc_methods_t * pcmk__attrd_api_methods(void)
Definition: ipc_attrd.c:117
#define PCMK__XA_ATTR_PATTERN
Definition: crm_internal.h:70
void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
Definition: nodes.c:15
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:83
Attribute manager.
Definition: ipc.h:70
#define PCMK__XA_ATTR_SET
Definition: crm_internal.h:73
#define crm_info(fmt, args...)
Definition: logging.h:362
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition: ipc_client.c:545
bool pcmk_ipc_is_connected(pcmk_ipc_api_t *api)
Check whether an IPC API connection is active.
Definition: ipc_client.c:283
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956