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