This source file includes following definitions.
- list_cb
- delete_cb
- attr_name_cb
- promotion_cb
- update_cb
- utilization_cb
- value_cb
- wait_cb
- get_node_name_from_local
- send_attrd_update
- delete_attr_on_node
- command_list
- command_delete
- update_attr_on_node
- command_update
- output_one_attribute
- command_query
- set_type
- use_attrd
- try_ipc_update
- pattern_used_correctly
- delete_used_correctly
- update_used_correctly
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <libgen.h>
19 #include <time.h>
20
21 #include <sys/param.h>
22 #include <sys/types.h>
23
24 #include <crm/crm.h>
25 #include <crm/common/xml.h>
26 #include <crm/common/ipc.h>
27 #include <crm/common/util.h>
28 #include <crm/cluster.h>
29
30 #include <crm/cib.h>
31 #include <crm/cib/internal.h>
32 #include <crm/common/attrs_internal.h>
33 #include <crm/common/cmdline_internal.h>
34 #include <crm/common/ipc_attrd_internal.h>
35 #include <crm/common/ipc_controld.h>
36 #include <crm/common/output_internal.h>
37 #include <sys/utsname.h>
38
39 #include <pacemaker-internal.h>
40
41 #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
42
43 enum attr_cmd {
44 attr_cmd_none,
45 attr_cmd_delete,
46 attr_cmd_list,
47 attr_cmd_query,
48 attr_cmd_update,
49 };
50
51 GError *error = NULL;
52 crm_exit_t exit_code = CRM_EX_OK;
53 uint64_t cib_opts = cib_sync_call;
54
55 static pcmk__supported_format_t formats[] = {
56 PCMK__SUPPORTED_FORMAT_NONE,
57 PCMK__SUPPORTED_FORMAT_TEXT,
58 PCMK__SUPPORTED_FORMAT_XML,
59 { NULL, NULL, NULL }
60 };
61
62 struct {
63 enum attr_cmd command;
64 gchar *attr_default;
65 gchar *attr_id;
66 gchar *attr_name;
67 uint32_t attr_options;
68 gchar *attr_pattern;
69 char *attr_value;
70 char *dest_node;
71 gchar *dest_uname;
72 gboolean inhibit;
73 gchar *set_name;
74 char *set_type;
75 gchar *type;
76 char *opt_list;
77 gboolean all;
78 bool promotion_score;
79 gboolean score_update;
80 } options = {
81 .command = attr_cmd_query,
82 };
83
84 #define INDENT " "
85
86 static gboolean
87 list_cb(const gchar *option_name, const gchar *optarg, gpointer data,
88 GError **error) {
89 options.command = attr_cmd_list;
90 pcmk__str_update(&options.opt_list, optarg);
91 return TRUE;
92 }
93
94 static gboolean
95 delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
96 options.command = attr_cmd_delete;
97 pcmk__str_update(&options.attr_value, NULL);
98 return TRUE;
99 }
100
101 static gboolean
102 attr_name_cb(const gchar *option_name, const gchar *optarg, gpointer data,
103 GError **error)
104 {
105 options.promotion_score = false;
106
107 if (options.attr_name != NULL) {
108 g_free(options.attr_name);
109 }
110 options.attr_name = g_strdup(optarg);
111 return TRUE;
112 }
113
114 static gboolean
115 promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
116 char *score_name = NULL;
117
118 options.promotion_score = true;
119
120 if (options.attr_name) {
121 g_free(options.attr_name);
122 }
123
124 score_name = pcmk_promotion_score_name(optarg);
125 if (score_name != NULL) {
126 options.attr_name = g_strdup(score_name);
127 free(score_name);
128 } else {
129 options.attr_name = NULL;
130 }
131
132 return TRUE;
133 }
134
135 static gboolean
136 update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
137 options.command = attr_cmd_update;
138 pcmk__str_update(&options.attr_value, optarg);
139 return TRUE;
140 }
141
142 static gboolean
143 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
144 if (options.type) {
145 g_free(options.type);
146 }
147
148 options.type = g_strdup(PCMK_XE_NODES);
149 pcmk__str_update(&options.set_type, PCMK_XE_UTILIZATION);
150 return TRUE;
151 }
152
153 static gboolean
154 value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
155 options.command = attr_cmd_query;
156 pcmk__str_update(&options.attr_value, NULL);
157 return TRUE;
158 }
159
160 static gboolean
161 wait_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
162 if (pcmk__str_eq(optarg, "no", pcmk__str_none)) {
163 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
164 return TRUE;
165 } else if (pcmk__str_eq(optarg, PCMK__VALUE_LOCAL, pcmk__str_none)) {
166 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
167 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local);
168 return TRUE;
169 } else if (pcmk__str_eq(optarg, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
170 pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_sync_local | pcmk__node_attr_sync_cluster);
171 pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_sync_cluster);
172 return TRUE;
173 } else {
174 g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE,
175 "--wait= must be one of 'no', 'local', 'cluster'");
176 return FALSE;
177 }
178 }
179
180 static GOptionEntry selecting_entries[] = {
181 { "all", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all,
182 "With -L/--list-options, include advanced and deprecated options in the\n"
183 INDENT "output. This is always treated as true when --output-as=xml is\n"
184 INDENT "specified.",
185 NULL,
186 },
187
188 { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
189 "(Advanced) Operate on instance of specified attribute with this\n"
190 INDENT "XML ID",
191 "XML_ID"
192 },
193
194 { "name", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, attr_name_cb,
195 "Operate on attribute or option with this name. For queries, this\n"
196 INDENT "is optional, in which case all matching attributes will be\n"
197 INDENT "returned.",
198 "NAME"
199 },
200
201 { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
202 "Operate on all attributes matching this pattern\n"
203 INDENT "(with -v, -D, or -G)",
204 "PATTERN"
205 },
206
207 { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb,
208 "Operate on node attribute used as promotion score for specified\n"
209 INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n"
210 INDENT "variable if none is specified; this also defaults -l/--lifetime\n"
211 INDENT "to reboot (normally invoked from an OCF resource agent)",
212 "RESOURCE"
213 },
214
215 { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name,
216 "(Advanced) Operate on instance of specified attribute that is\n"
217 INDENT "within set with this XML ID",
218 "NAME"
219 },
220
221 { NULL }
222 };
223
224 static GOptionEntry command_entries[] = {
225 { "list-options", 'L', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, list_cb,
226 "List all available options of the given type.\n"
227 INDENT "Allowed values: " PCMK__VALUE_CLUSTER,
228 "TYPE"
229 },
230
231 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
232 "Delete the attribute/option (with -n or -P)",
233 NULL
234 },
235
236 { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
237 "Query the current value of the attribute/option.\n"
238 INDENT "See also: -n, -P",
239 NULL
240 },
241
242 { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
243 "Update the value of the attribute/option (with -n or -P)",
244 "VALUE"
245 },
246
247 { NULL }
248 };
249
250 static GOptionEntry addl_entries[] = {
251 { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
252 "(Advanced) Default value to display if none is found in configuration",
253 "VALUE"
254 },
255
256 { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type,
257 "Lifetime of the node attribute.\n"
258 INDENT "Valid values: reboot, forever",
259 "LIFETIME"
260 },
261
262 { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname,
263 "Set a node attribute for named node (instead of a cluster option).\n"
264 INDENT "See also: -l",
265 "NODE"
266 },
267
268 { "type", 't', 0, G_OPTION_ARG_STRING, &options.type,
269 "Which part of the configuration to update/delete/query the option in.\n"
270 INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets",
271 "SECTION"
272 },
273
274 { "score", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.score_update,
275 "Treat new attribute values as atomic score updates where possible\n"
276 INDENT "(with --update/-v, when running against a CIB file or updating\n"
277 INDENT "an attribute outside the " PCMK_XE_STATUS " section; enabled\n"
278 INDENT "by default if --promotion/-p is specified)\n\n"
279
280 INDENT "This currently happens by default and cannot be disabled, but\n"
281 INDENT "this default behavior is deprecated and will be removed in a\n"
282 INDENT "future release (exception: this will remain the default with\n"
283 INDENT "--promotion/-p). Set this flag if this behavior is desired.\n\n"
284
285 INDENT "This option takes effect when updating XML attributes. For an\n"
286 INDENT "attribute named \"name\", if the new value is \"name++\" or\n"
287 INDENT "\"name+=X\" for some score X, the new value is set as follows:\n"
288 INDENT " * If attribute \"name\" is not already set to some value in\n"
289 INDENT " the element being updated, the new value is set as a literal\n"
290 INDENT " string.\n"
291 INDENT " * If the new value is \"name++\", then the attribute is set to\n"
292 INDENT " its existing value (parsed as a score) plus 1.\n"
293 INDENT " * If the new value is \"name+=X\" for some score X, then the\n"
294 INDENT " attribute is set to its existing value plus X, where the\n"
295 INDENT " existing value and X are parsed and added as scores.\n\n"
296
297 INDENT "Scores are integer values capped at INFINITY and -INFINITY.\n"
298 INDENT "Refer to Pacemaker Explained and to the char2score() function\n"
299 INDENT "for more details on scores, including how they're parsed and\n"
300 INDENT "added.",
301 NULL },
302
303 { "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb,
304 "Wait for some event to occur before returning. Values are 'no' (wait\n"
305 INDENT "only for the attribute daemon to acknowledge the request),\n"
306 INDENT "'local' (wait until the change has propagated to where a local\n"
307 INDENT "query will return the request value, or the value set by a\n"
308 INDENT "later request), or 'cluster' (wait until the change has propagated\n"
309 INDENT "to where a query anywhere on the cluster will return the requested\n"
310 INDENT "value, or the value set by a later request). Default is 'no'.\n"
311 INDENT "(with -N, and one of -D or -u)",
312 "UNTIL" },
313
314 { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
315 "Set an utilization attribute for the node.",
316 NULL
317 },
318
319 { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
320 NULL, NULL
321 },
322
323 { NULL }
324 };
325
326 static GOptionEntry deprecated_entries[] = {
327 { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
328 NULL, NULL
329 },
330
331 { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, attr_name_cb,
332 NULL, NULL
333 },
334
335 { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb,
336 NULL, NULL
337 },
338
339 { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb,
340 NULL, NULL
341 },
342
343 { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
344 NULL, NULL
345 },
346
347 { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
348 NULL, NULL
349 },
350
351 { NULL }
352 };
353
354 static void
355 get_node_name_from_local(void)
356 {
357 struct utsname hostinfo;
358
359 g_free(options.dest_uname);
360
361 if (uname(&hostinfo) == 0) {
362 options.dest_uname = g_strdup(hostinfo.nodename);
363 } else {
364 options.dest_uname = NULL;
365 }
366 }
367
368 static int
369 send_attrd_update(enum attr_cmd command, const char *attr_node,
370 const char *attr_name, const char *attr_value,
371 const char *attr_set, const char *attr_dampen,
372 uint32_t attr_options)
373 {
374 int rc = pcmk_rc_ok;
375 uint32_t opts = attr_options;
376
377 switch (command) {
378 case attr_cmd_delete:
379 rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, opts);
380 break;
381
382 case attr_cmd_update:
383 rc = pcmk__attrd_api_update(NULL, attr_node, attr_name,
384 attr_value, NULL, attr_set, NULL,
385 opts | pcmk__node_attr_value);
386 break;
387
388 default:
389 break;
390 }
391
392 if (rc != pcmk_rc_ok) {
393 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
394 attr_name, attr_value, pcmk_rc_str(rc), rc);
395 }
396
397 return rc;
398 }
399
400 struct delete_data_s {
401 pcmk__output_t *out;
402 cib_t *cib;
403 };
404
405 static int
406 delete_attr_on_node(xmlNode *child, void *userdata)
407 {
408 struct delete_data_s *dd = (struct delete_data_s *) userdata;
409
410 const char *attr_name = crm_element_value(child, PCMK_XA_NAME);
411 int rc = pcmk_rc_ok;
412
413 if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
414 return pcmk_rc_ok;
415 }
416
417 rc = cib__delete_node_attr(dd->out, dd->cib, cib_opts, options.type,
418 options.dest_node, options.set_type,
419 options.set_name, options.attr_id,
420 attr_name, options.attr_value, NULL);
421
422 if (rc == ENXIO) {
423 rc = pcmk_rc_ok;
424 }
425
426 return rc;
427 }
428
429 static void
430 command_list(pcmk__output_t *out)
431 {
432 if (pcmk__str_eq(options.opt_list, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
433 exit_code = pcmk_rc2exitc(pcmk__list_cluster_options(out, options.all));
434
435 } else {
436
437 exit_code = CRM_EX_USAGE;
438 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
439 "Invalid --list-options value '%s'. Allowed values: "
440 PCMK__VALUE_CLUSTER,
441 pcmk__s(options.opt_list, "(BUG: none)"));
442 }
443 }
444
445 static int
446 command_delete(pcmk__output_t *out, cib_t *cib)
447 {
448 int rc = pcmk_rc_ok;
449
450 xmlNode *result = NULL;
451 bool use_pattern = options.attr_pattern != NULL;
452
453
454 if (use_pattern) {
455 struct delete_data_s dd = { out, cib };
456
457 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
458 options.set_type, options.set_name, NULL, NULL,
459 NULL, &result);
460
461 if (rc != pcmk_rc_ok) {
462 goto done_deleting;
463 }
464
465 rc = pcmk__xe_foreach_child(result, NULL, delete_attr_on_node, &dd);
466
467 } else {
468 rc = cib__delete_node_attr(out, cib, cib_opts, options.type, options.dest_node,
469 options.set_type, options.set_name, options.attr_id,
470 options.attr_name, options.attr_value, NULL);
471 }
472
473 done_deleting:
474 free_xml(result);
475
476 if (rc == ENXIO) {
477
478
479
480
481 rc = pcmk_rc_ok;
482 }
483
484 return rc;
485 }
486
487 struct update_data_s {
488 pcmk__output_t *out;
489 cib_t *cib;
490 int is_remote_node;
491 };
492
493 static int
494 update_attr_on_node(xmlNode *child, void *userdata)
495 {
496 struct update_data_s *ud = (struct update_data_s *) userdata;
497
498 const char *attr_name = crm_element_value(child, PCMK_XA_NAME);
499
500 if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
501 return pcmk_rc_ok;
502 }
503
504 return cib__update_node_attr(ud->out, ud->cib, cib_opts, options.type,
505 options.dest_node, options.set_type,
506 options.set_name, options.attr_id,
507 attr_name, options.attr_value, NULL,
508 ud->is_remote_node? PCMK_VALUE_REMOTE : NULL);
509 }
510
511 static int
512 command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node)
513 {
514 int rc = pcmk_rc_ok;
515
516 xmlNode *result = NULL;
517 bool use_pattern = options.attr_pattern != NULL;
518
519
520
521
522 cib__set_call_options(cib_opts, crm_system_name, cib_score_update);
523
524
525 if (use_pattern) {
526 struct update_data_s ud = { out, cib, is_remote_node };
527
528 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
529 options.set_type, options.set_name, NULL, NULL,
530 NULL, &result);
531
532 if (rc != pcmk_rc_ok) {
533 goto done_updating;
534 }
535
536 rc = pcmk__xe_foreach_child(result, NULL, update_attr_on_node, &ud);
537
538 } else {
539 rc = cib__update_node_attr(out, cib, cib_opts, options.type,
540 options.dest_node, options.set_type,
541 options.set_name, options.attr_id,
542 options.attr_name, options.attr_value, NULL,
543 is_remote_node? PCMK_VALUE_REMOTE : NULL);
544 }
545
546 done_updating:
547 free_xml(result);
548 return rc;
549 }
550
551 struct output_data_s {
552 pcmk__output_t *out;
553 bool use_pattern;
554 bool did_output;
555 };
556
557 static int
558 output_one_attribute(xmlNode *node, void *userdata)
559 {
560 struct output_data_s *od = (struct output_data_s *) userdata;
561
562 const char *name = crm_element_value(node, PCMK_XA_NAME);
563 const char *value = crm_element_value(node, PCMK_XA_VALUE);
564
565 const char *type = options.type;
566 const char *attr_id = options.attr_id;
567
568 if (od->use_pattern && !pcmk__str_eq(name, options.attr_pattern, pcmk__str_regex)) {
569 return pcmk_rc_ok;
570 }
571
572 od->out->message(od->out, "attribute", type, attr_id, name, value, NULL,
573 od->out->quiet, true);
574 od->did_output = true;
575 crm_info("Read %s='%s' %s%s",
576 pcmk__s(name, "<null>"), pcmk__s(value, ""),
577 options.set_name ? "in " : "", options.set_name ? options.set_name : "");
578
579 return pcmk_rc_ok;
580 }
581
582 static int
583 command_query(pcmk__output_t *out, cib_t *cib)
584 {
585 int rc = pcmk_rc_ok;
586
587 xmlNode *result = NULL;
588 bool use_pattern = options.attr_pattern != NULL;
589
590
591
592
593
594
595 if (use_pattern) {
596 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
597 options.set_type, options.set_name, NULL,
598 NULL, NULL, &result);
599 } else {
600 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
601 options.set_type, options.set_name, options.attr_id,
602 options.attr_name, NULL, &result);
603 }
604
605 if (rc == ENXIO && options.attr_default) {
606
607 const char *type = options.type;
608 const char *attr_id = options.attr_id;
609 const char *attr_name = options.attr_name;
610 const char *attr_default = options.attr_default;
611
612 out->message(out, "attribute", type, attr_id, attr_name, attr_default,
613 NULL, out->quiet, true);
614 rc = pcmk_rc_ok;
615
616 } else if (rc != pcmk_rc_ok) {
617
618
619 } else if (result->children != NULL) {
620 struct output_data_s od = { out, use_pattern, false };
621
622 pcmk__xe_foreach_child(result, NULL, output_one_attribute, &od);
623
624 if (!od.did_output) {
625 rc = ENXIO;
626 }
627
628 } else {
629 struct output_data_s od = { out, use_pattern, false };
630 output_one_attribute(result, &od);
631 }
632
633 free_xml(result);
634 return rc;
635 }
636
637 static void
638 set_type(void)
639 {
640 if (options.type == NULL) {
641 if (options.promotion_score) {
642
643 options.type = g_strdup(PCMK_XE_STATUS);
644
645 } else if (options.dest_uname != NULL) {
646
647 options.type = g_strdup(PCMK_XE_NODES);
648
649 } else {
650
651 options.type = g_strdup(PCMK_XE_CRM_CONFIG);
652 }
653
654 } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
655 options.type = g_strdup(PCMK_XE_STATUS);
656
657 } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
658 options.type = g_strdup(PCMK_XE_NODES);
659 }
660 }
661
662 static bool
663 use_attrd(void)
664 {
665
666
667
668 return pcmk__str_eq(options.type, PCMK_XE_STATUS, pcmk__str_casei) &&
669 getenv("CIB_file") == NULL && getenv("CIB_shadow") == NULL;
670 }
671
672 static bool
673 try_ipc_update(void)
674 {
675 return use_attrd()
676 && ((options.command == attr_cmd_delete)
677 || (options.command == attr_cmd_update));
678 }
679
680 static bool
681 pattern_used_correctly(void)
682 {
683
684
685
686 switch (options.command) {
687 case attr_cmd_delete:
688 case attr_cmd_query:
689 case attr_cmd_update:
690 return true;
691 default:
692 return false;
693 }
694 }
695
696 static bool
697 delete_used_correctly(void)
698 {
699 return (options.command != attr_cmd_delete)
700 || (options.attr_name != NULL)
701 || (options.attr_pattern != NULL);
702 }
703
704 static bool
705 update_used_correctly(void)
706 {
707 return (options.command != attr_cmd_update)
708 || (options.attr_name != NULL)
709 || (options.attr_pattern != NULL);
710 }
711
712 static GOptionContext *
713 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
714 GOptionContext *context = NULL;
715
716 GOptionEntry extra_prog_entries[] = {
717 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
718 "Print only the value on stdout",
719 NULL },
720
721 { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
722 NULL, NULL
723 },
724
725 { NULL }
726 };
727
728 const char *description = "Examples:\n\n"
729 "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
730 "\tcrm_attribute --node myhost --name location --update office\n\n"
731 "Query the value of the 'location' node attribute for host 'myhost':\n\n"
732 "\tcrm_attribute --node myhost --name location --query\n\n"
733 "Change the value of the 'location' node attribute for host 'myhost':\n\n"
734 "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
735 "Delete the 'location' node attribute for host 'myhost':\n\n"
736 "\tcrm_attribute --node myhost --name location --delete\n\n"
737 "Query the value of the '" PCMK_OPT_CLUSTER_DELAY
738 "' cluster option:\n\n"
739 "\tcrm_attribute --type crm_config --name "
740 PCMK_OPT_CLUSTER_DELAY " --query\n\n"
741 "Query value of the '" PCMK_OPT_CLUSTER_DELAY
742 "' cluster option and print only the value:\n\n"
743 "\tcrm_attribute --type crm_config --name "
744 PCMK_OPT_CLUSTER_DELAY " --query --quiet\n\n";
745
746 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
747 pcmk__add_main_args(context, extra_prog_entries);
748 g_option_context_set_description(context, description);
749
750 pcmk__add_arg_group(context, "selections", "Selecting attributes:",
751 "Show selecting options", selecting_entries);
752 pcmk__add_arg_group(context, "command", "Commands:",
753 "Show command options", command_entries);
754 pcmk__add_arg_group(context, "additional", "Additional options:",
755 "Show additional options", addl_entries);
756 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
757 "Show deprecated options", deprecated_entries);
758
759 return context;
760 }
761
762 int
763 main(int argc, char **argv)
764 {
765 cib_t *the_cib = NULL;
766 int is_remote_node = 0;
767
768 int rc = pcmk_rc_ok;
769
770 pcmk__output_t *out = NULL;
771
772 GOptionGroup *output_group = NULL;
773 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
774 gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv");
775 GOptionContext *context = build_arg_context(args, &output_group);
776
777 pcmk__register_formats(output_group, formats);
778 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
779 exit_code = CRM_EX_USAGE;
780 goto done;
781 }
782
783 pcmk__cli_init_logging("crm_attribute", args->verbosity);
784
785 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
786 if (rc != pcmk_rc_ok) {
787 exit_code = CRM_EX_ERROR;
788 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
789 args->output_ty, pcmk_rc_str(rc));
790 goto done;
791 }
792
793 pcmk__register_lib_messages(out);
794
795 if (args->version) {
796 out->version(out, false);
797 goto done;
798 }
799
800 out->quiet = args->quiet;
801
802 if (options.command == attr_cmd_list) {
803 command_list(out);
804 goto done;
805 }
806
807 if (options.promotion_score && options.attr_name == NULL) {
808 exit_code = CRM_EX_USAGE;
809 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
810 "-p/--promotion must be called from an OCF resource agent "
811 "or with a resource ID specified");
812 goto done;
813 }
814
815 if (options.inhibit) {
816 crm_warn("Inhibiting notifications for this update");
817 cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
818 }
819
820 the_cib = cib_new();
821 rc = cib__signon_attempts(the_cib, crm_system_name, cib_command, 5);
822 rc = pcmk_legacy2rc(rc);
823
824 if (rc != pcmk_rc_ok) {
825 exit_code = pcmk_rc2exitc(rc);
826 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
827 "Could not connect to the CIB: %s", pcmk_rc_str(rc));
828 goto done;
829 }
830
831 set_type();
832
833
834 if (!pcmk__strcase_any_of(options.type,
835 PCMK_XE_CRM_CONFIG, PCMK_XE_TICKETS, NULL)) {
836
837
838
839
840 const char *target = pcmk__node_attr_target(options.dest_uname);
841
842 if (target != NULL) {
843
844
845
846
847
848
849
850
851
852
853
854
855 if (target != (const char *) options.dest_uname) {
856 g_free(options.dest_uname);
857 options.dest_uname = g_strdup(target);
858 }
859
860 } else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) {
861 get_node_name_from_local();
862 }
863
864 if (options.dest_uname == NULL) {
865 char *node_name = NULL;
866
867 rc = pcmk__query_node_name(out, 0, &node_name, 0);
868
869 if (rc != pcmk_rc_ok) {
870 exit_code = pcmk_rc2exitc(rc);
871 free(node_name);
872 goto done;
873 }
874 options.dest_uname = g_strdup(node_name);
875 free(node_name);
876 }
877
878 rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
879 rc = pcmk_legacy2rc(rc);
880
881 if (rc != pcmk_rc_ok) {
882 exit_code = pcmk_rc2exitc(rc);
883 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
884 "Could not map name=%s to a UUID", options.dest_uname);
885 goto done;
886 }
887 }
888
889 if (!delete_used_correctly()) {
890 exit_code = CRM_EX_USAGE;
891 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
892 "Error: must specify attribute name or pattern to delete");
893 goto done;
894 }
895
896 if (!update_used_correctly()) {
897 exit_code = CRM_EX_USAGE;
898 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
899 "Error: must specify attribute name or pattern to update");
900 goto done;
901 }
902
903 if (options.attr_pattern) {
904 if (options.attr_name) {
905 exit_code = CRM_EX_USAGE;
906 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
907 "Error: --name and --pattern cannot be used at the same time");
908 goto done;
909 }
910
911 if (!pattern_used_correctly()) {
912 exit_code = CRM_EX_USAGE;
913 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
914 "Error: pattern can only be used with delete, query, or update");
915 goto done;
916 }
917
918 g_free(options.attr_name);
919 options.attr_name = options.attr_pattern;
920 options.attr_options |= pcmk__node_attr_pattern;
921 }
922
923 if (is_remote_node) {
924 options.attr_options |= pcmk__node_attr_remote;
925 }
926
927 if (pcmk__str_eq(options.set_type, PCMK_XE_UTILIZATION, pcmk__str_none)) {
928 options.attr_options |= pcmk__node_attr_utilization;
929 }
930
931 if (try_ipc_update() &&
932 (send_attrd_update(options.command, options.dest_uname, options.attr_name,
933 options.attr_value, options.set_name, NULL, options.attr_options) == pcmk_rc_ok)) {
934
935 const char *update = options.attr_value;
936
937 if (options.command == attr_cmd_delete) {
938 update = "<none>";
939 }
940 crm_info("Update %s=%s sent via pacemaker-attrd",
941 options.attr_name, update);
942
943 } else if (options.command == attr_cmd_delete) {
944 rc = command_delete(out, the_cib);
945
946 } else if (options.command == attr_cmd_update) {
947 rc = command_update(out, the_cib, is_remote_node);
948
949 } else {
950 rc = command_query(out, the_cib);
951 }
952
953 if (rc == ENOTUNIQ) {
954 exit_code = pcmk_rc2exitc(rc);
955 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
956 "Please choose from one of the matches below and supply the 'id' with --attr-id");
957
958 } else if (rc != pcmk_rc_ok) {
959 exit_code = pcmk_rc2exitc(rc);
960 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
961 "Error performing operation: %s", pcmk_rc_str(rc));
962 }
963
964 done:
965 g_strfreev(processed_args);
966 pcmk__free_arg_context(context);
967
968 free(options.attr_default);
969 g_free(options.attr_id);
970 g_free(options.attr_name);
971 free(options.attr_value);
972 free(options.dest_node);
973 g_free(options.dest_uname);
974 g_free(options.set_name);
975 free(options.set_type);
976 g_free(options.type);
977
978 cib__clean_up_connection(&the_cib);
979
980 pcmk__output_and_clear_error(&error, out);
981
982 if (out != NULL) {
983 out->finish(out, exit_code, true, NULL);
984 pcmk__output_free(out);
985 }
986
987 pcmk__unregister_formats();
988 return crm_exit(exit_code);
989 }