pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
ipc_attrd.c
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2024 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 #include <stdio.h>
13 
14 #include <crm/crm.h>
16 #include <crm/common/ipc.h>
18 #include <crm/common/xml.h>
19 #include "crmcommon_private.h"
20 
21 static void
22 set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
23 {
24  const char *name = NULL;
26 
28 
29  for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL,
30  NULL);
31  node != NULL; node = pcmk__xe_next(node, PCMK_XE_NODE)) {
32 
34 
36  pair->name = name;
38  data->data.pairs = g_list_prepend(data->data.pairs, pair);
39  }
40 }
41 
42 static bool
43 reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
44 {
45  const char *command = crm_element_value(request, PCMK_XA_TASK);
46 
47  return pcmk__str_any_of(command,
54  NULL);
55 }
56 
57 static bool
58 dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
59 {
60  const char *value = NULL;
61  crm_exit_t status = CRM_EX_OK;
62 
63  pcmk__attrd_api_reply_t reply_data = {
65  };
66 
67  if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
68  return false;
69  }
70 
71  /* Do some basic validation of the reply */
72  value = crm_element_value(reply, PCMK__XA_T);
73  if (pcmk__str_empty(value)
74  || !pcmk__str_eq(value, PCMK__VALUE_ATTRD, pcmk__str_none)) {
75  crm_info("Unrecognizable message from attribute manager: "
76  "message type '%s' not '" PCMK__VALUE_ATTRD "'",
77  pcmk__s(value, ""));
78  status = CRM_EX_PROTOCOL;
79  goto done;
80  }
81 
82  value = crm_element_value(reply, PCMK__XA_SUBT);
83 
84  /* Only the query command gets a reply for now. NULL counts as query for
85  * backward compatibility with attribute managers <2.1.3 that didn't set it.
86  */
87  if (pcmk__str_eq(value, PCMK__ATTRD_CMD_QUERY, pcmk__str_null_matches)) {
88  if (!xmlHasProp(reply, (pcmkXmlStr) PCMK__XA_ATTR_NAME)) {
89  status = ENXIO; // Most likely, the attribute doesn't exist
90  goto done;
91  }
93  set_pairs_data(&reply_data, reply);
94 
95  } else {
96  crm_info("Unrecognizable message from attribute manager: "
97  "message subtype '%s' unknown", pcmk__s(value, ""));
98  status = CRM_EX_PROTOCOL;
99  goto done;
100  }
101 
102 done:
103  pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
104 
105  /* Free any reply data that was allocated */
106  if (reply_data.data.pairs) {
107  g_list_free_full(reply_data.data.pairs, free);
108  }
109 
110  return false;
111 }
112 
115 {
116  pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
117 
118  if (cmds != NULL) {
119  cmds->new_data = NULL;
120  cmds->free_data = NULL;
121  cmds->post_connect = NULL;
122  cmds->reply_expected = reply_expected;
123  cmds->dispatch = dispatch;
124  }
125  return cmds;
126 }
127 
136 static xmlNode *
137 create_attrd_op(const char *user_name)
138 {
139  xmlNode *attrd_op = pcmk__xe_create(NULL, __func__);
140 
142  crm_xml_add(attrd_op, PCMK__XA_SRC, pcmk__s(crm_system_name, "unknown"));
143  crm_xml_add(attrd_op, PCMK__XA_ATTR_USER, user_name);
144 
145  return attrd_op;
146 }
147 
148 static int
149 connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
150 {
151  int rc = pcmk_rc_ok;
152  bool created_api = false;
154 
155  if (api == NULL) {
156  rc = pcmk_new_ipc_api(&api, pcmk_ipc_attrd);
157  if (rc != pcmk_rc_ok) {
158  return rc;
159  }
160  created_api = true;
161  } else {
162  dispatch = api->dispatch_type;
163  }
164 
165  rc = pcmk__connect_ipc(api, dispatch, 5);
166  if (rc == pcmk_rc_ok) {
167  rc = pcmk__send_ipc_request(api, request);
168  }
169 
170  if (created_api) {
171  pcmk_free_ipc_api(api);
172  }
173  return rc;
174 }
175 
176 int
178  const char *resource, const char *operation,
179  const char *interval_spec, const char *user_name,
180  uint32_t options)
181 {
182  int rc = pcmk_rc_ok;
183  xmlNode *request = create_attrd_op(user_name);
184  const char *interval_desc = NULL;
185  const char *op_desc = NULL;
186  const char *target = pcmk__node_attr_target(node);
187 
188  if (target != NULL) {
189  node = target;
190  }
191 
192  if (operation) {
193  interval_desc = pcmk__s(interval_spec, "nonrecurring");
194  op_desc = operation;
195  } else {
196  interval_desc = "all";
197  op_desc = "operations";
198  }
199  crm_debug("Asking %s to clear failure of %s %s for %s on %s",
200  pcmk_ipc_name(api, true), interval_desc, op_desc,
201  pcmk__s(resource, "all resources"), pcmk__s(node, "all nodes"));
202 
204  pcmk__xe_add_node(request, node, 0);
205  crm_xml_add(request, PCMK__XA_ATTR_RESOURCE, resource);
206  crm_xml_add(request, PCMK__XA_ATTR_CLEAR_OPERATION, operation);
207  crm_xml_add(request, PCMK__XA_ATTR_CLEAR_INTERVAL, interval_spec);
210 
211  rc = connect_and_send_attrd_request(api, request);
212 
213  pcmk__xml_free(request);
214  return rc;
215 }
216 
217 int
218 pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name,
219  uint32_t options)
220 {
221  const char *target = NULL;
222 
223  if (name == NULL) {
224  return EINVAL;
225  }
226 
228 
229  if (target != NULL) {
230  node = target;
231  }
232 
233  /* Make sure the right update option is set. */
234  options &= ~pcmk__node_attr_delay;
235  options |= pcmk__node_attr_value;
236 
237  return pcmk__attrd_api_update(api, node, name, NULL, NULL, NULL, NULL, options);
238 }
239 
240 int
241 pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap)
242 {
243  int rc = pcmk_rc_ok;
244  xmlNode *request = NULL;
245  const char *target = pcmk__node_attr_target(node);
246 
247  if (target != NULL) {
248  node = target;
249  }
250 
251  crm_debug("Asking %s to purge transient attributes%s for %s",
252  pcmk_ipc_name(api, true),
253  (reap? " and node cache entries" : ""),
254  pcmk__s(node, "local node"));
255 
256  request = create_attrd_op(NULL);
257 
259  pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, reap);
260  pcmk__xe_add_node(request, node, 0);
261 
262  rc = connect_and_send_attrd_request(api, request);
263 
264  pcmk__xml_free(request);
265  return rc;
266 }
267 
268 int
269 pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name,
270  uint32_t options)
271 {
272  int rc = pcmk_rc_ok;
273  xmlNode *request = NULL;
274  const char *target = NULL;
275 
276  if (name == NULL) {
277  return EINVAL;
278  }
279 
280  if (pcmk_is_set(options, pcmk__node_attr_query_all)) {
281  node = NULL;
282  } else {
284 
285  if (target != NULL) {
286  node = target;
287  } else if (node == NULL) {
288  node = "localhost";
289  }
290  }
291 
292  crm_debug("Querying %s for value of '%s'%s%s",
293  pcmk_ipc_name(api, true), name,
294  ((node == NULL)? "" : " on "), pcmk__s(node, ""));
295 
296  request = create_attrd_op(NULL);
297 
300  pcmk__xe_add_node(request, node, 0);
301 
302  rc = connect_and_send_attrd_request(api, request);
303  pcmk__xml_free(request);
304  return rc;
305 }
306 
307 int
309 {
310  int rc = pcmk_rc_ok;
311  xmlNode *request = NULL;
312  const char *target = pcmk__node_attr_target(node);
313 
314  if (target != NULL) {
315  node = target;
316  }
317 
318  crm_debug("Asking %s to write all transient attributes for %s to CIB",
319  pcmk_ipc_name(api, true), pcmk__s(node, "local node"));
320 
321  request = create_attrd_op(NULL);
322 
324  pcmk__xe_add_node(request, node, 0);
325 
326  rc = connect_and_send_attrd_request(api, request);
327 
328  pcmk__xml_free(request);
329  return rc;
330 }
331 
332 static void
333 add_op_attr(xmlNode *op, uint32_t options)
334 {
335  if (pcmk_all_flags_set(options, pcmk__node_attr_value | pcmk__node_attr_delay)) {
337  } else if (pcmk_is_set(options, pcmk__node_attr_value)) {
339  } else if (pcmk_is_set(options, pcmk__node_attr_delay)) {
341  }
342 }
343 
344 static void
345 populate_update_op(xmlNode *op, const char *node, const char *name, const char *value,
346  const char *dampen, const char *set, uint32_t options)
347 {
348  if (pcmk_is_set(options, pcmk__node_attr_pattern)) {
350  } else {
352  }
353 
356  } else {
358  }
359 
360  add_op_attr(op, options);
361 
362  crm_xml_add(op, PCMK__XA_ATTR_VALUE, value);
364  pcmk__xe_add_node(op, node, 0);
365  crm_xml_add(op, PCMK__XA_ATTR_SET, set);
370 
371  if (pcmk_is_set(options, pcmk__node_attr_sync_local)) {
373  } else if (pcmk_is_set(options, pcmk__node_attr_sync_cluster)) {
375  }
376 }
377 
378 int
379 pcmk__attrd_api_update(pcmk_ipc_api_t *api, const char *node, const char *name,
380  const char *value, const char *dampen, const char *set,
381  const char *user_name, uint32_t options)
382 {
383  int rc = pcmk_rc_ok;
384  xmlNode *request = NULL;
385  const char *target = NULL;
386 
387  if (name == NULL) {
388  return EINVAL;
389  }
390 
392 
393  if (target != NULL) {
394  node = target;
395  }
396 
397  crm_debug("Asking %s to update '%s' to '%s' for %s",
398  pcmk_ipc_name(api, true), name, pcmk__s(value, "(null)"),
399  pcmk__s(node, "local node"));
400 
401  request = create_attrd_op(user_name);
402  populate_update_op(request, node, name, value, dampen, set, options);
403 
404  rc = connect_and_send_attrd_request(api, request);
405 
406  pcmk__xml_free(request);
407  return rc;
408 }
409 
410 int
411 pcmk__attrd_api_update_list(pcmk_ipc_api_t *api, GList *attrs, const char *dampen,
412  const char *set, const char *user_name,
413  uint32_t options)
414 {
415  int rc = pcmk_rc_ok;
416  xmlNode *request = NULL;
417 
418  if (attrs == NULL) {
419  return EINVAL;
420  }
421 
422  /* There are two different ways of handling a list of attributes:
423  *
424  * (1) For messages originating from some command line tool, we have to send
425  * them one at a time. In this loop, we just call pcmk__attrd_api_update
426  * for each, letting it deal with creating the API object if it doesn't
427  * already exist.
428  *
429  * The reason we can't use a single message in this case is that we can't
430  * trust that the server supports it. Remote nodes could be involved
431  * here, and there's no guarantee that a newer client running on a remote
432  * node is talking to (or proxied through) a cluster node with a newer
433  * attrd. We also can't just try sending a single message and then falling
434  * back on multiple. There's no handshake with the attrd server to
435  * determine its version. And then we would need to do that fallback in the
436  * dispatch function for this to work for all connection types (mainloop in
437  * particular), and at that point we won't know what the original message
438  * was in order to break it apart and resend as individual messages.
439  *
440  * (2) For messages between daemons, we can be assured that the local attrd
441  * will support the new message and that it can send to the other attrds
442  * as one request or split up according to the minimum supported version.
443  */
444  for (GList *iter = attrs; iter != NULL; iter = iter->next) {
445  pcmk__attrd_query_pair_t *pair = (pcmk__attrd_query_pair_t *) iter->data;
446 
447  if (pcmk__is_daemon) {
448  const char *target = NULL;
449  xmlNode *child = NULL;
450 
451  /* First time through this loop - create the basic request. */
452  if (request == NULL) {
453  request = create_attrd_op(user_name);
454  add_op_attr(request, options);
455  }
456 
457  /* Add a child node for this operation. We add the task to the top
458  * level XML node so attrd_ipc_dispatch doesn't need changes. And
459  * then we also add the task to each child node in populate_update_op
460  * so attrd_client_update knows what form of update is taking place.
461  */
462  child = pcmk__xe_create(request, PCMK_XE_OP);
464 
465  if (target != NULL) {
466  pair->node = target;
467  }
468 
469  populate_update_op(child, pair->node, pair->name, pair->value, dampen,
470  set, options);
471  } else {
472  rc = pcmk__attrd_api_update(api, pair->node, pair->name, pair->value,
473  dampen, set, user_name, options);
474  }
475  }
476 
477  /* If we were doing multiple attributes at once, we still need to send the
478  * request. Do that now, creating and destroying the API object if needed.
479  */
480  if (pcmk__is_daemon) {
481  rc = connect_and_send_attrd_request(api, request);
482  pcmk__xml_free(request);
483  }
484 
485  return rc;
486 }
#define PCMK__ATTRD_CMD_UPDATE_DELAY
Definition: crm_internal.h:83
int pcmk__attrd_api_query(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:269
A dumping ground.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
#define PCMK__ATTRD_CMD_UPDATE_BOTH
Definition: crm_internal.h:82
char data[0]
Definition: cpg.c:58
#define PCMK__XA_ATTR_CLEAR_INTERVAL
enum pcmk_ipc_dispatch dispatch_type
#define PCMK__ATTRD_CMD_PEER_REMOVE
Definition: crm_internal.h:80
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: xml_element.c:1480
#define PCMK__VALUE_ATTRD
const char * name
Definition: cib.c:26
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:411
int pcmk__attrd_api_refresh(pcmk_ipc_api_t *api, const char *node)
Definition: ipc_attrd.c:308
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:46
char * crm_system_name
Definition: utils.c:44
enum crm_exit_e crm_exit_t
#define PCMK__XA_SUBT
#define PCMK__ATTRD_CMD_CLEAR_FAILURE
Definition: crm_internal.h:87
#define PCMK__XA_ATTR_REGEX
const char * pcmk__node_attr_target(const char *name)
Definition: attrs.c:38
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:150
int pcmk__connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type, int attempts)
Definition: ipc_client.c:491
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml_element.c:407
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:177
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
#define PCMK__XA_ATTR_NAME
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:379
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap)
Definition: ipc_attrd.c:241
#define PCMK__XA_ATTR_CLEAR_OPERATION
#define PCMK__XA_ATTR_SYNC_POINT
Protocol violated.
Definition: results.h:257
union pcmk__attrd_api_reply_t::@0 data
#define crm_debug(fmt, args...)
Definition: logging.h:370
#define PCMK__ATTRD_CMD_REFRESH
Definition: crm_internal.h:85
#define PCMK_XE_UTILIZATION
Definition: xml_names.h:217
G_GNUC_INTERNAL int pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request)
Definition: ipc_client.c:658
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
#define PCMK_XA_TASK
Definition: xml_names.h:424
bool pcmk__is_daemon
Definition: logging.c:47
Wrappers for and extensions to libxml2.
#define PCMK__XA_ATTR_IS_PRIVATE
Success.
Definition: results.h:231
#define PCMK__ATTRD_CMD_QUERY
Definition: crm_internal.h:84
#define PCMK__XA_ATTR_DAMPENING
#define PCMK__XA_ATTR_RESOURCE
#define PCMK__ATTRD_CMD_UPDATE
Definition: crm_internal.h:81
#define PCMK__XA_ATTR_SET_TYPE
int(* post_connect)(pcmk_ipc_api_t *api)
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1051
#define PCMK__XA_ATTR_SET
enum pcmk__attrd_api_reply reply_type
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
Definition: xml_element.c:106
const xmlChar * pcmkXmlStr
Definition: xml.h:41
const char * target
Definition: pcmk_fence.c:31
#define PCMK__XA_ATTR_HOST
int(* new_data)(pcmk_ipc_api_t *api)
pcmk_ipc_dispatch
How IPC replies should be dispatched.
Definition: ipc.h:74
int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *name, uint32_t options)
Definition: ipc_attrd.c:218
#define PCMK_XE_OP
Definition: xml_names.h:146
#define PCMK_XE_NODE
Definition: xml_names.h:136
bool(* dispatch)(pcmk_ipc_api_t *api, xmlNode *msg)
bool(* reply_expected)(pcmk_ipc_api_t *api, const xmlNode *request)
Sending a command will wait for any reply.
Definition: ipc.h:77
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition: ipc_client.c:206
#define PCMK__XA_ATTR_VALUE
#define PCMK__XE_ACK
#define PCMK__VALUE_CLUSTER
void(* free_data)(void *api_data)
const char * pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log)
Get the IPC name used with an IPC API connection.
Definition: ipc_client.c:247
IPC interface to Pacemaker daemons.
pcmk__ipc_methods_t * pcmk__attrd_api_methods(void)
Definition: ipc_attrd.c:114
#define PCMK__XA_ATTR_USER
#define PCMK__XA_ATTR_IS_REMOTE
#define PCMK__XA_T
void pcmk__xe_add_node(xmlNode *xml, const char *node, int nodeid)
Definition: nodes.c:147
Daemon&#39;s reply to client IPC request.
Definition: ipc.h:68
#define PCMK__XA_REAP
Attribute manager.
Definition: ipc.h:50
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:257
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition: xml_names.h:122
#define PCMK__VALUE_LOCAL
#define crm_info(fmt, args...)
Definition: logging.h:367
#define PCMK__XA_SRC
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: xml_element.c:1070