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