This source file includes following definitions.
- command_cb
- private_cb
- section_cb
- attr_set_type_cb
- wait_cb
- pattern_used_correctly
- build_arg_context
- main
- print_attrd_values
- attrd_event_cb
- send_attrd_query
- send_attrd_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/ipc_attrd_internal.h>
23 #include <crm/common/cmdline_internal.h>
24 #include <crm/common/output_internal.h>
25 #include <crm/common/xml_internal.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 bool printed_values = false;
42
43 struct {
44 char command;
45 gchar *attr_dampen;
46 gchar *attr_name;
47 gchar *attr_pattern;
48 gchar *attr_node;
49 gchar *attr_set;
50 char *attr_value;
51 uint32_t attr_options;
52 gboolean query_all;
53 gboolean quiet;
54 } options = {
55 .attr_options = pcmk__node_attr_none,
56 .command = 'Q',
57 };
58
59 static gboolean
60 command_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
61 pcmk__str_update(&options.attr_value, optarg);
62
63 if (pcmk__str_any_of(option_name, "--update-both", "-B", NULL)) {
64 options.command = 'B';
65 } else if (pcmk__str_any_of(option_name, "--delete", "-D", NULL)) {
66 options.command = 'D';
67 } else if (pcmk__str_any_of(option_name, "--query", "-Q", NULL)) {
68 options.command = 'Q';
69 } else if (pcmk__str_any_of(option_name, "--refresh", "-R", NULL)) {
70 options.command = 'R';
71 } else if (pcmk__str_any_of(option_name, "--update", "-U", "-v", NULL)) {
72 options.command = 'U';
73 } else if (pcmk__str_any_of(option_name, "--update-delay", "-Y", NULL)) {
74 options.command = 'Y';
75 }
76
77 return TRUE;
78 }
79
80 static gboolean
81 private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
82 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_private);
83 return TRUE;
84 }
85
86 static gboolean
87 section_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
88 if (pcmk__str_any_of(optarg, "nodes", "forever", NULL)) {
89 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
90 } else if (pcmk__str_any_of(optarg, "status", "reboot", NULL)) {
91 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
92 } else {
93 g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE, "Unknown value for --lifetime: %s",
94 optarg);
95 return FALSE;
96 }
97
98 return TRUE;
99 }
100
101 static gboolean
102 attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
103 if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) {
104 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_utilization);
105 }
106
107 return TRUE;
108 }
109
110 static gboolean
111 wait_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
112 if (pcmk__str_eq(optarg, "no", pcmk__str_none)) {
113 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
114 return TRUE;
115 } else if (pcmk__str_eq(optarg, PCMK__VALUE_LOCAL, pcmk__str_none)) {
116 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
117 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local);
118 return TRUE;
119 } else if (pcmk__str_eq(optarg, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
120 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
121 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_cluster);
122 return TRUE;
123 } else {
124 g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE,
125 "--wait= must be one of 'no', 'local', 'cluster'");
126 return FALSE;
127 }
128 }
129
130 #define INDENT " "
131
132 static GOptionEntry required_entries[] = {
133 { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
134 "The attribute's name",
135 "NAME" },
136
137 { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
138 "Operate on all attributes matching this pattern\n"
139 INDENT "(with -B, -D, -U, or -Y)",
140 "PATTERN"
141 },
142
143 { NULL }
144 };
145
146 static GOptionEntry command_entries[] = {
147 { "update", 'U', 0, G_OPTION_ARG_CALLBACK, command_cb,
148 "Update attribute's value. Required: -n/--name or -P/--pattern.\n"
149 INDENT "Optional: -d/--delay (if specified, the delay will be used if\n"
150 INDENT "the attribute needs to be created, but ignored if the\n"
151 INDENT "attribute already exists), -s/--set, -p/--private, -W/--wait,\n"
152 INDENT "-z/--utilization.",
153 "VALUE" },
154
155 { "update-both", 'B', 0, G_OPTION_ARG_CALLBACK, command_cb,
156 "Update attribute's value and time to wait (dampening) in\n"
157 INDENT "pacemaker-attrd. If this causes the value or dampening to change,\n"
158 INDENT "the attribute will also be written to the cluster configuration,\n"
159 INDENT "so be aware that repeatedly changing the dampening reduces its\n"
160 INDENT "effectiveness.\n"
161 INDENT "Requires -d/--delay",
162 "VALUE" },
163
164 { "update-delay", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
165 "Update attribute's dampening in pacemaker-attrd. If this causes\n"
166 INDENT "the dampening to change, the attribute will also be written\n"
167 INDENT "to the cluster configuration, so be aware that repeatedly\n"
168 INDENT "changing the dampening reduces its effectiveness.\n"
169 INDENT "Requires -d/--delay",
170 NULL },
171
172 { "query", 'Q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
173 "Query the attribute's value from pacemaker-attrd",
174 NULL },
175
176 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
177 "Unset attribute from pacemaker-attrd. At the moment, there is no way\n"
178 INDENT "to remove an attribute. This option will instead set its value\n"
179 INDENT "to the empty string.",
180 NULL },
181
182 { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
183 "(Advanced) Force the pacemaker-attrd daemon to resend all current\n"
184 INDENT "values to the CIB",
185 NULL },
186
187 { NULL }
188 };
189
190 static GOptionEntry addl_entries[] = {
191 { "delay", 'd', 0, G_OPTION_ARG_STRING, &options.attr_dampen,
192 "The time to wait (dampening) in seconds for further changes\n"
193 INDENT "before sending to the CIB",
194 "SECONDS" },
195
196 { "set", 's', 0, G_OPTION_ARG_STRING, &options.attr_set,
197 "(Advanced) The attribute set in which to place the value",
198 "SET" },
199
200 { "node", 'N', 0, G_OPTION_ARG_STRING, &options.attr_node,
201 "Set the attribute for the named node (instead of the local one)",
202 "NODE" },
203
204 { "all", 'A', 0, G_OPTION_ARG_NONE, &options.query_all,
205 "Show values of the attribute for all nodes (query only)",
206 NULL },
207
208 { "lifetime", 'l', 0, G_OPTION_ARG_CALLBACK, section_cb,
209 "(Not yet implemented) Lifetime of the node attribute (silently\n"
210 INDENT "ignored by cluster)",
211 "SECTION" },
212
213 { "private", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, private_cb,
214 "If this creates a new attribute, never write the attribute to CIB",
215 NULL },
216
217 { "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb,
218 "Wait for some event to occur before returning. Values are 'no' (wait\n"
219 INDENT "only for the attribute daemon to acknowledge the request),\n"
220 INDENT "'local' (wait until the change has propagated to where a local\n"
221 INDENT "query will return the request value, or the value set by a\n"
222 INDENT "later request), or 'cluster' (wait until the change has propagated\n"
223 INDENT "to where a query anywhere on the cluster will return the requested\n"
224 INDENT "value, or the value set by a later request). Default is 'no'.",
225 "UNTIL" },
226
227 { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, attr_set_type_cb,
228 "When creating a new attribute, create it as a node utilization attribute\n"
229 INDENT "instead of an instance attribute. If the attribute already exists,\n"
230 INDENT "its existing type (utilization vs. instance) will be used regardless.\n"
231 INDENT "(with -B, -U, -Y)",
232 NULL },
233
234 { NULL }
235 };
236
237 static GOptionEntry deprecated_entries[] = {
238 { "quiet", 'q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.quiet,
239 NULL,
240 NULL },
241
242 { "update", 'v', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, command_cb,
243 NULL,
244 NULL },
245
246 { "section", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, section_cb,
247 NULL,
248 NULL },
249
250 { NULL }
251 };
252
253 static int send_attrd_query(pcmk__output_t *out, const char *attr_name, const char *attr_node,
254 gboolean query_all);
255 static int send_attrd_update(char command, const char *attr_node, const char *attr_name,
256 const char *attr_value, const char *attr_set,
257 const char *attr_dampen, uint32_t attr_options);
258
259 static bool
260 pattern_used_correctly(void)
261 {
262
263
264
265 return options.command == 'B' || options.command == 'D' || options.command == 'U' || options.command == 'Y';
266 }
267
268 static GOptionContext *
269 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
270 GOptionContext *context = NULL;
271
272 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
273
274 pcmk__add_arg_group(context, "required", "Required Arguments:",
275 "Show required arguments", required_entries);
276 pcmk__add_arg_group(context, "command", "Command:",
277 "Show command options (mutually exclusive)", command_entries);
278 pcmk__add_arg_group(context, "additional", "Additional Options:",
279 "Show additional options", addl_entries);
280 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
281 "Show deprecated options", deprecated_entries);
282
283 return context;
284 }
285
286 int
287 main(int argc, char **argv)
288 {
289 int rc = pcmk_rc_ok;
290 crm_exit_t exit_code = CRM_EX_OK;
291
292 pcmk__output_t *out = NULL;
293
294 GOptionGroup *output_group = NULL;
295 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
296 GOptionContext *context = build_arg_context(args, &output_group);
297 gchar **processed_args = pcmk__cmdline_preproc(argv, "dlnsvBNUS");
298
299 pcmk__register_formats(output_group, formats);
300 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
301 exit_code = CRM_EX_USAGE;
302 goto done;
303 }
304
305 pcmk__cli_init_logging("attrd_updater", args->verbosity);
306
307 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
308 if (rc != pcmk_rc_ok) {
309 exit_code = CRM_EX_ERROR;
310 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
311 args->output_ty, pcmk_rc_str(rc));
312 goto done;
313 }
314
315 if (args->version) {
316 out->version(out, false);
317 goto done;
318 }
319
320 if (options.attr_pattern) {
321 if (options.attr_name) {
322 exit_code = CRM_EX_USAGE;
323 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
324 "Error: --name and --pattern cannot be used at the same time");
325 goto done;
326 }
327
328 if (!pattern_used_correctly()) {
329 exit_code = CRM_EX_USAGE;
330 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
331 "Error: pattern can only be used with delete or update");
332 goto done;
333 }
334
335 g_free(options.attr_name);
336 options.attr_name = options.attr_pattern;
337 options.attr_options |= pcmk__node_attr_pattern;
338 }
339
340 if (options.command != 'R' && options.attr_name == NULL) {
341 exit_code = CRM_EX_USAGE;
342 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Command requires --name or --pattern argument");
343 goto done;
344 } else if ((options.command == 'B'|| options.command == 'Y') && options.attr_dampen == NULL) {
345 out->info(out, "Warning: '%c' command given without required --delay", options.command);
346 }
347
348 pcmk__register_lib_messages(out);
349
350 if (options.command == 'Q') {
351 int rc = send_attrd_query(out, options.attr_name, options.attr_node, options.query_all);
352 exit_code = pcmk_rc2exitc(rc);
353 } else {
354
355
356
357
358
359 int rc = send_attrd_update(options.command, options.attr_node,
360 options.attr_name, options.attr_value,
361 options.attr_set, options.attr_dampen,
362 options.attr_options);
363 exit_code = pcmk_rc2exitc(rc);
364 }
365
366 done:
367 g_strfreev(processed_args);
368 pcmk__free_arg_context(context);
369 g_free(options.attr_dampen);
370 g_free(options.attr_name);
371 g_free(options.attr_node);
372 g_free(options.attr_set);
373 free(options.attr_value);
374
375 pcmk__output_and_clear_error(&error, out);
376
377 if (out != NULL) {
378 out->finish(out, exit_code, true, NULL);
379 pcmk__output_free(out);
380 }
381
382 pcmk__unregister_formats();
383 crm_exit(exit_code);
384 }
385
386
387
388
389
390
391
392
393
394 static void
395 print_attrd_values(pcmk__output_t *out, const GList *reply)
396 {
397 for (const GList *iter = reply; iter != NULL; iter = iter->next) {
398 const pcmk__attrd_query_pair_t *pair = iter->data;
399
400 out->message(out, "attribute", NULL, NULL, pair->name, pair->value,
401 pair->node);
402 printed_values = true;
403 }
404 }
405
406 static void
407 attrd_event_cb(pcmk_ipc_api_t *attrd_api, enum pcmk_ipc_event event_type,
408 crm_exit_t status, void *event_data, void *user_data)
409 {
410 pcmk__output_t *out = (pcmk__output_t *) user_data;
411 pcmk__attrd_api_reply_t *reply = event_data;
412
413 if (event_type != pcmk_ipc_event_reply || status != CRM_EX_OK) {
414 return;
415 }
416
417
418 if (reply->reply_type == pcmk__attrd_reply_query) {
419 print_attrd_values(out, reply->data.pairs);
420 }
421 }
422
423
424
425
426
427
428
429
430
431
432
433 static int
434 send_attrd_query(pcmk__output_t *out, const char *attr_name,
435 const char *attr_node, gboolean query_all)
436 {
437 uint32_t options = pcmk__node_attr_none;
438 pcmk_ipc_api_t *attrd_api = NULL;
439 int rc = pcmk_rc_ok;
440
441
442 rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd);
443 if (rc != pcmk_rc_ok) {
444 g_set_error(&error, PCMK__RC_ERROR, rc,
445 "Could not connect to attrd: %s", pcmk_rc_str(rc));
446 return ENOTCONN;
447 }
448
449 pcmk_register_ipc_callback(attrd_api, attrd_event_cb, out);
450
451
452 rc = pcmk__connect_ipc(attrd_api, pcmk_ipc_dispatch_sync, 5);
453 if (rc != pcmk_rc_ok) {
454 g_set_error(&error, PCMK__RC_ERROR, rc,
455 "Could not connect to %s: %s",
456 pcmk_ipc_name(attrd_api, true), pcmk_rc_str(rc));
457 pcmk_free_ipc_api(attrd_api);
458 return rc;
459 }
460
461
462 if (query_all == TRUE) {
463 options |= pcmk__node_attr_query_all;
464 }
465
466 rc = pcmk__attrd_api_query(attrd_api, attr_node, attr_name, options);
467
468 if (rc != pcmk_rc_ok) {
469 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not query value of %s: %s (%d)",
470 attr_name, pcmk_rc_str(rc), rc);
471 } else if (!printed_values) {
472 rc = pcmk_rc_schema_validation;
473 g_set_error(&error, PCMK__RC_ERROR, rc,
474 "Could not query value of %s: attribute does not exist", attr_name);
475 }
476
477 pcmk_disconnect_ipc(attrd_api);
478 pcmk_free_ipc_api(attrd_api);
479
480 return rc;
481 }
482
483 static int
484 send_attrd_update(char command, const char *attr_node, const char *attr_name,
485 const char *attr_value, const char *attr_set,
486 const char *attr_dampen, uint32_t attr_options)
487 {
488 int rc = pcmk_rc_ok;
489
490 switch (command) {
491 case 'B':
492 rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value,
493 attr_dampen, attr_set, NULL,
494 attr_options | pcmk__node_attr_value | pcmk__node_attr_delay);
495 break;
496
497 case 'D':
498 rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, attr_options);
499 break;
500
501 case 'R':
502 rc = pcmk__attrd_api_refresh(NULL, attr_node);
503 break;
504
505 case 'U':
506 rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, attr_value,
507 attr_dampen, attr_set, NULL,
508 attr_options | pcmk__node_attr_value);
509 break;
510
511 case 'Y':
512 rc = pcmk__attrd_api_update(NULL, attr_node, attr_name, NULL,
513 attr_dampen, attr_set, NULL,
514 attr_options | pcmk__node_attr_delay);
515 break;
516 }
517
518 if (rc != pcmk_rc_ok) {
519 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
520 attr_name, attr_value, pcmk_rc_str(rc), rc);
521 }
522
523 return rc;
524 }