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