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