This source file includes following definitions.
- command_cb
- private_cb
- build_arg_context
- main
- send_attrd_query
- validate_attrd_reply
- print_attrd_values
- do_query
- do_update
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <libgen.h>
16
17 #include <sys/param.h>
18 #include <sys/types.h>
19
20 #include <crm/crm.h>
21 #include <crm/msg_xml.h>
22 #include <crm/common/cmdline_internal.h>
23 #include <crm/common/output_internal.h>
24 #include <crm/common/xml_internal.h>
25 #include <crm/common/ipc.h>
26
27 #include <crm/common/attrd_internal.h>
28
29 #include <pcmki/pcmki_output.h>
30
31 #define SUMMARY "query and update Pacemaker node attributes"
32
33 static pcmk__supported_format_t formats[] = {
34 PCMK__SUPPORTED_FORMAT_NONE,
35 PCMK__SUPPORTED_FORMAT_TEXT,
36 PCMK__SUPPORTED_FORMAT_XML,
37 { NULL, NULL, NULL }
38 };
39
40 GError *error = NULL;
41
42 struct {
43 char command;
44 gchar *attr_dampen;
45 gchar *attr_name;
46 gchar *attr_node;
47 gchar *attr_section;
48 gchar *attr_set;
49 char *attr_value;
50 int attr_options;
51 gboolean query_all;
52 } options = {
53 .attr_options = pcmk__node_attr_none,
54 .command = 'Q',
55 };
56
57 static gboolean
58 command_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
59 pcmk__str_update(&options.attr_value, optarg);
60
61 if (pcmk__str_any_of(option_name, "--update-both", "-B", NULL)) {
62 options.command = 'B';
63 } else if (pcmk__str_any_of(option_name, "--delete", "-D", NULL)) {
64 options.command = 'D';
65 } else if (pcmk__str_any_of(option_name, "--query", "-Q", NULL)) {
66 options.command = 'Q';
67 } else if (pcmk__str_any_of(option_name, "--refresh", "-R", NULL)) {
68 options.command = 'R';
69 } else if (pcmk__str_any_of(option_name, "--update", "-U", "-v", NULL)) {
70 options.command = 'U';
71 } else if (pcmk__str_any_of(option_name, "--update-delay", "-Y", NULL)) {
72 options.command = 'Y';
73 }
74
75 return TRUE;
76 }
77
78 static gboolean
79 private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
80 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_private);
81 return TRUE;
82 }
83
84 #define INDENT " "
85
86 static GOptionEntry required_entries[] = {
87 { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
88 "The attribute's name",
89 "NAME" },
90
91 { NULL }
92 };
93
94 static GOptionEntry command_entries[] = {
95 { "update", 'U', 0, G_OPTION_ARG_CALLBACK, command_cb,
96 "Update attribute's value in pacemaker-attrd. If this causes the value\n"
97 INDENT "to change, it will also be updated in the cluster configuration.",
98 "VALUE" },
99
100 { "update-both", 'B', 0, G_OPTION_ARG_CALLBACK, command_cb,
101 "Update attribute's value and time to wait (dampening) in\n"
102 INDENT "pacemaker-attrd. If this causes the value or dampening to change,\n"
103 INDENT "the attribute will also be written to the cluster configuration,\n"
104 INDENT "so be aware that repeatedly changing the dampening reduces its\n"
105 INDENT "effectiveness.",
106 "VALUE" },
107
108 { "update-delay", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
109 "Update attribute's dampening in pacemaker-attrd (requires\n"
110 INDENT "-d/--delay). If this causes the dampening to change, the\n"
111 INDENT "attribute will also be written to the cluster configuration, so\n"
112 INDENT "be aware that repeatedly changing the dampening reduces its\n"
113 INDENT "effectiveness.",
114 NULL },
115
116 { "query", 'Q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
117 "Query the attribute's value from pacemaker-attrd",
118 NULL },
119
120 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
121 "Unset attribute from pacemaker-attrd. At the moment, there is no way\n"
122 INDENT "to remove an attribute. This option will instead set its value\n"
123 INDENT "to the empty string.",
124 NULL },
125
126 { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
127 "(Advanced) Force the pacemaker-attrd daemon to resend all current\n"
128 INDENT "values to the CIB",
129 NULL },
130
131 { NULL }
132 };
133
134 static GOptionEntry addl_entries[] = {
135 { "delay", 'd', 0, G_OPTION_ARG_STRING, &options.attr_dampen,
136 "The time to wait (dampening) in seconds for further changes\n"
137 INDENT "before sending to the CIB",
138 "SECONDS" },
139
140 { "set", 's', 0, G_OPTION_ARG_STRING, &options.attr_set,
141 "(Advanced) The attribute set in which to place the value",
142 "SET" },
143
144 { "node", 'N', 0, G_OPTION_ARG_STRING, &options.attr_node,
145 "Set the attribute for the named node (instead of the local one)",
146 "NODE" },
147
148 { "all", 'A', 0, G_OPTION_ARG_NONE, &options.query_all,
149 "Show values of the attribute for all nodes (query only)",
150 NULL },
151
152 { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.attr_section,
153 "(Not yet implemented) Lifetime of the node attribute (silently\n"
154 INDENT "ignored by cluster)",
155 "SECTION" },
156
157 { "private", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, private_cb,
158 "If this creates a new attribute, never write the attribute to CIB",
159 NULL },
160
161 { NULL }
162 };
163
164 static GOptionEntry deprecated_entries[] = {
165 { "update", 'v', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb,
166 NULL,
167 NULL },
168
169 { "section", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_section,
170 NULL,
171 NULL },
172
173 { NULL }
174 };
175
176 static int do_query(pcmk__output_t *out, const char *attr_name, const char *attr_node,
177 gboolean query_all);
178 static int do_update(char command, const char *attr_node, const char *attr_name,
179 const char *attr_value, const char *attr_section,
180 const char *attr_set, const char *attr_dampen, int attr_options);
181
182 static GOptionContext *
183 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
184 GOptionContext *context = NULL;
185
186 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
187
188 pcmk__add_arg_group(context, "required", "Required Arguments:",
189 "Show required arguments", required_entries);
190 pcmk__add_arg_group(context, "command", "Command:",
191 "Show command options (mutually exclusive)", command_entries);
192 pcmk__add_arg_group(context, "additional", "Additional Options:",
193 "Show additional options", addl_entries);
194 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
195 "Show deprecated options", deprecated_entries);
196
197 return context;
198 }
199
200 int
201 main(int argc, char **argv)
202 {
203 int rc = pcmk_rc_ok;
204 crm_exit_t exit_code = CRM_EX_OK;
205
206 pcmk__output_t *out = NULL;
207
208 GOptionGroup *output_group = NULL;
209 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
210 GOptionContext *context = build_arg_context(args, &output_group);
211 gchar **processed_args = pcmk__cmdline_preproc(argv, "dlnsvBNUS");
212
213 pcmk__register_formats(output_group, formats);
214 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
215 exit_code = CRM_EX_USAGE;
216 goto done;
217 }
218
219 pcmk__cli_init_logging("attrd_updater", args->verbosity);
220
221 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
222 if (rc != pcmk_rc_ok) {
223 exit_code = CRM_EX_ERROR;
224 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
225 args->output_ty, pcmk_rc_str(rc));
226 goto done;
227 }
228
229 if (args->version) {
230 out->version(out, false);
231 goto done;
232 }
233
234 if (options.command != 'R' && options.attr_name == NULL) {
235 exit_code = CRM_EX_USAGE;
236 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Command requires --name argument");
237 goto done;
238 }
239
240 pcmk__register_lib_messages(out);
241
242 if (options.command == 'Q') {
243 int rc = do_query(out, options.attr_name, options.attr_node, options.query_all);
244 exit_code = pcmk_rc2exitc(rc);
245 } else {
246
247
248
249
250
251 const char *target = pcmk__node_attr_target(options.attr_node);
252
253 exit_code = pcmk_rc2exitc(do_update(options.command,
254 target == NULL ? options.attr_node : target,
255 options.attr_name, options.attr_value,
256 options.attr_section, options.attr_set,
257 options.attr_dampen, options.attr_options));
258 }
259
260 done:
261 g_strfreev(processed_args);
262 pcmk__free_arg_context(context);
263 g_free(options.attr_dampen);
264 g_free(options.attr_name);
265 g_free(options.attr_node);
266 g_free(options.attr_section);
267 g_free(options.attr_set);
268 free(options.attr_value);
269
270 pcmk__output_and_clear_error(error, out);
271
272 if (out != NULL) {
273 out->finish(out, exit_code, true, NULL);
274 pcmk__output_free(out);
275 }
276
277 crm_exit(exit_code);
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291 static int
292 send_attrd_query(const char *name, const char *host, xmlNode **reply)
293 {
294 int rc = pcmk_rc_ok;
295 crm_ipc_t *ipc;
296 xmlNode *query;
297
298
299 query = create_xml_node(NULL, __func__);
300 if (query == NULL) {
301 return ENOMEM;
302 }
303 crm_xml_add(query, F_TYPE, T_ATTRD);
304 crm_xml_add(query, F_ORIG, crm_system_name);
305 crm_xml_add(query, PCMK__XA_ATTR_NODE_NAME, host);
306 crm_xml_add(query, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY);
307 crm_xml_add(query, PCMK__XA_ATTR_NAME, name);
308
309
310 crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes"));
311 ipc = crm_ipc_new(T_ATTRD, 0);
312 if (!crm_ipc_connect(ipc)) {
313 crm_perror(LOG_ERR, "Connection to cluster attribute manager failed");
314 rc = ENOTCONN;
315 } else {
316 rc = crm_ipc_send(ipc, query, crm_ipc_client_response, 0, reply);
317 if (rc > 0) {
318 rc = pcmk_rc_ok;
319 }
320 crm_ipc_close(ipc);
321 }
322 crm_ipc_destroy(ipc);
323
324 free_xml(query);
325 return(rc);
326 }
327
328
329
330
331
332
333
334
335
336
337 static int
338 validate_attrd_reply(xmlNode *reply, const char *attr_name)
339 {
340 int rc = pcmk_rc_ok;
341 const char *reply_attr;
342
343 if (reply == NULL) {
344 rc = pcmk_rc_schema_validation;
345 g_set_error(&error, PCMK__RC_ERROR, rc,
346 "Could not query value of %s: reply did not contain valid XML",
347 attr_name);
348 return rc;
349 }
350 crm_log_xml_trace(reply, "Reply");
351
352 reply_attr = crm_element_value(reply, PCMK__XA_ATTR_NAME);
353 if (reply_attr == NULL) {
354 rc = ENXIO;
355 g_set_error(&error, PCMK__RC_ERROR, rc,
356 "Could not query value of %s: attribute does not exist",
357 attr_name);
358 return rc;
359 }
360
361 if (!pcmk__str_eq(crm_element_value(reply, F_TYPE), T_ATTRD, pcmk__str_casei)
362 || (crm_element_value(reply, PCMK__XA_ATTR_VERSION) == NULL)
363 || strcmp(reply_attr, attr_name)) {
364 rc = pcmk_rc_schema_validation;
365 g_set_error(&error, PCMK__RC_ERROR, rc,
366 "Could not query value of %s: reply did not contain expected identification",
367 attr_name);
368 return rc;
369 }
370
371 return pcmk_rc_ok;
372 }
373
374
375
376
377
378
379
380
381
382 static bool
383 print_attrd_values(pcmk__output_t *out, xmlNode *reply, const char *attr_name)
384 {
385 xmlNode *child;
386 const char *reply_host, *reply_value;
387 bool have_values = false;
388
389
390 for (child = pcmk__xml_first_child(reply); child != NULL;
391 child = pcmk__xml_next(child)) {
392
393 if (!pcmk__str_eq((const char *)child->name, XML_CIB_TAG_NODE,
394 pcmk__str_casei)) {
395 crm_warn("Ignoring unexpected %s tag in query reply", child->name);
396 } else {
397 reply_host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
398 reply_value = crm_element_value(child, PCMK__XA_ATTR_VALUE);
399
400 if (reply_host == NULL) {
401 crm_warn("Ignoring %s tag without %s attribute in query reply",
402 XML_CIB_TAG_NODE, PCMK__XA_ATTR_NODE_NAME);
403 } else {
404 out->message(out, "attribute", NULL, NULL, attr_name, reply_value, reply_host);
405 have_values = true;
406 }
407 }
408 }
409
410 return have_values;
411 }
412
413
414
415
416
417
418
419
420
421
422 static int
423 do_query(pcmk__output_t *out, const char *attr_name, const char *attr_node, gboolean query_all)
424 {
425 xmlNode *reply = NULL;
426 int rc = pcmk_rc_ok;
427
428
429 if (query_all == TRUE) {
430 attr_node = NULL;
431 } else {
432 const char *target = pcmk__node_attr_target(attr_node);
433 if (target != NULL) {
434 attr_node = target;
435 }
436 }
437
438
439 rc = send_attrd_query(attr_name, attr_node, &reply);
440 if (rc != pcmk_rc_ok) {
441 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not query value of %s: %s (%d)",
442 attr_name, pcmk_strerror(rc), rc);
443 return rc;
444 }
445
446
447 rc = validate_attrd_reply(reply, attr_name);
448 if (rc != pcmk_rc_ok) {
449 if (reply != NULL) {
450 free_xml(reply);
451 }
452 return rc;
453 }
454
455
456 if (!print_attrd_values(out, reply, attr_name)) {
457 g_set_error(&error, PCMK__RC_ERROR, rc,
458 "Could not query value of %s: reply had attribute name but no host values",
459 attr_name);
460 free_xml(reply);
461 return pcmk_rc_schema_validation;
462 }
463
464 return pcmk_rc_ok;
465 }
466
467 static int
468 do_update(char command, const char *attr_node, const char *attr_name,
469 const char *attr_value, const char *attr_section,
470 const char *attr_set, const char *attr_dampen, int attr_options)
471 {
472 int rc = pcmk__node_attr_request(NULL, command, attr_node, attr_name,
473 attr_value, attr_section, attr_set,
474 attr_dampen, NULL, attr_options);
475 if (rc != pcmk_rc_ok) {
476 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
477 attr_name, attr_value, pcmk_rc_str(rc), rc);
478 }
479 return rc;
480 }