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 for more details on scores,\n"
299 INDENT "including how they are parsed and added.",
300 NULL },
301
302 { "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb,
303 "Wait for some event to occur before returning. Values are 'no' (wait\n"
304 INDENT "only for the attribute daemon to acknowledge the request),\n"
305 INDENT "'local' (wait until the change has propagated to where a local\n"
306 INDENT "query will return the request value, or the value set by a\n"
307 INDENT "later request), or 'cluster' (wait until the change has propagated\n"
308 INDENT "to where a query anywhere on the cluster will return the requested\n"
309 INDENT "value, or the value set by a later request). Default is 'no'.\n"
310 INDENT "(with -N, and one of -D or -u)",
311 "UNTIL" },
312
313 { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
314 "Set an utilization attribute for the node.",
315 NULL
316 },
317
318 { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
319 NULL, NULL
320 },
321
322 { NULL }
323 };
324
325 static GOptionEntry deprecated_entries[] = {
326 { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
327 NULL, NULL
328 },
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
344 { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
345 NULL, NULL
346 },
347
348
349 { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
350 NULL, NULL
351 },
352
353 { NULL }
354 };
355
356 static void
357 get_node_name_from_local(void)
358 {
359 struct utsname hostinfo;
360
361 g_free(options.dest_uname);
362
363 if (uname(&hostinfo) == 0) {
364 options.dest_uname = g_strdup(hostinfo.nodename);
365 } else {
366 options.dest_uname = NULL;
367 }
368 }
369
370 static int
371 send_attrd_update(enum attr_cmd command, const char *attr_node,
372 const char *attr_name, const char *attr_value,
373 const char *attr_set, const char *attr_dampen,
374 uint32_t attr_options)
375 {
376 int rc = pcmk_rc_ok;
377 uint32_t opts = attr_options;
378
379 switch (command) {
380 case attr_cmd_delete:
381 rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, opts);
382 break;
383
384 case attr_cmd_update:
385 rc = pcmk__attrd_api_update(NULL, attr_node, attr_name,
386 attr_value, NULL, attr_set, NULL,
387 opts | pcmk__node_attr_value);
388 break;
389
390 default:
391 break;
392 }
393
394 if (rc != pcmk_rc_ok) {
395 g_set_error(&error, PCMK__RC_ERROR, rc, "Could not update %s=%s: %s (%d)",
396 attr_name, attr_value, pcmk_rc_str(rc), rc);
397 }
398
399 return rc;
400 }
401
402 struct delete_data_s {
403 pcmk__output_t *out;
404 cib_t *cib;
405 };
406
407 static int
408 delete_attr_on_node(xmlNode *child, void *userdata)
409 {
410 struct delete_data_s *dd = (struct delete_data_s *) userdata;
411
412 const char *attr_name = crm_element_value(child, PCMK_XA_NAME);
413 int rc = pcmk_rc_ok;
414
415 if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
416 return pcmk_rc_ok;
417 }
418
419 rc = cib__delete_node_attr(dd->out, dd->cib, cib_opts, options.type,
420 options.dest_node, options.set_type,
421 options.set_name, options.attr_id,
422 attr_name, options.attr_value, NULL);
423
424 if (rc == ENXIO) {
425 rc = pcmk_rc_ok;
426 }
427
428 return rc;
429 }
430
431 static void
432 command_list(pcmk__output_t *out)
433 {
434 if (pcmk__str_eq(options.opt_list, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
435 exit_code = pcmk_rc2exitc(pcmk__list_cluster_options(out, options.all));
436
437 } else {
438
439 exit_code = CRM_EX_USAGE;
440 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
441 "Invalid --list-options value '%s'. Allowed values: "
442 PCMK__VALUE_CLUSTER,
443 pcmk__s(options.opt_list, "(BUG: none)"));
444 }
445 }
446
447 static int
448 command_delete(pcmk__output_t *out, cib_t *cib)
449 {
450 int rc = pcmk_rc_ok;
451
452 xmlNode *result = NULL;
453 bool use_pattern = options.attr_pattern != NULL;
454
455
456 if (use_pattern) {
457 struct delete_data_s dd = { out, cib };
458
459 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
460 options.set_type, options.set_name, NULL, NULL,
461 NULL, &result);
462
463 if (rc != pcmk_rc_ok) {
464 goto done_deleting;
465 }
466
467 rc = pcmk__xe_foreach_child(result, NULL, delete_attr_on_node, &dd);
468
469 } else {
470 rc = cib__delete_node_attr(out, cib, cib_opts, options.type, options.dest_node,
471 options.set_type, options.set_name, options.attr_id,
472 options.attr_name, options.attr_value, NULL);
473 }
474
475 done_deleting:
476 pcmk__xml_free(result);
477
478 if (rc == ENXIO) {
479
480
481
482
483 rc = pcmk_rc_ok;
484 }
485
486 return rc;
487 }
488
489 struct update_data_s {
490 pcmk__output_t *out;
491 cib_t *cib;
492 int is_remote_node;
493 };
494
495 static int
496 update_attr_on_node(xmlNode *child, void *userdata)
497 {
498 struct update_data_s *ud = (struct update_data_s *) userdata;
499
500 const char *attr_name = crm_element_value(child, PCMK_XA_NAME);
501
502 if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
503 return pcmk_rc_ok;
504 }
505
506 return cib__update_node_attr(ud->out, ud->cib, cib_opts, options.type,
507 options.dest_node, options.set_type,
508 options.set_name, options.attr_id,
509 attr_name, options.attr_value, NULL,
510 ud->is_remote_node? PCMK_VALUE_REMOTE : NULL);
511 }
512
513 static int
514 command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node)
515 {
516 int rc = pcmk_rc_ok;
517
518 xmlNode *result = NULL;
519 bool use_pattern = options.attr_pattern != NULL;
520
521
522
523
524 cib__set_call_options(cib_opts, crm_system_name, cib_score_update);
525
526
527 if (use_pattern) {
528 struct update_data_s ud = { out, cib, is_remote_node };
529
530 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
531 options.set_type, options.set_name, NULL, NULL,
532 NULL, &result);
533
534 if (rc != pcmk_rc_ok) {
535 goto done_updating;
536 }
537
538 rc = pcmk__xe_foreach_child(result, NULL, update_attr_on_node, &ud);
539
540 } else {
541 rc = cib__update_node_attr(out, cib, cib_opts, options.type,
542 options.dest_node, options.set_type,
543 options.set_name, options.attr_id,
544 options.attr_name, options.attr_value, NULL,
545 is_remote_node? PCMK_VALUE_REMOTE : NULL);
546 }
547
548 done_updating:
549 pcmk__xml_free(result);
550 return rc;
551 }
552
553 struct output_data_s {
554 pcmk__output_t *out;
555 bool use_pattern;
556 bool did_output;
557 };
558
559 static int
560 output_one_attribute(xmlNode *node, void *userdata)
561 {
562 struct output_data_s *od = (struct output_data_s *) userdata;
563
564 const char *name = crm_element_value(node, PCMK_XA_NAME);
565 const char *value = crm_element_value(node, PCMK_XA_VALUE);
566
567 const char *type = options.type;
568 const char *attr_id = options.attr_id;
569
570 if (od->use_pattern && !pcmk__str_eq(name, options.attr_pattern, pcmk__str_regex)) {
571 return pcmk_rc_ok;
572 }
573
574 od->out->message(od->out, "attribute", type, attr_id, name, value, NULL,
575 od->out->quiet, true);
576 od->did_output = true;
577 crm_info("Read %s='%s' %s%s",
578 pcmk__s(name, "<null>"), pcmk__s(value, ""),
579 options.set_name ? "in " : "", options.set_name ? options.set_name : "");
580
581 return pcmk_rc_ok;
582 }
583
584 static int
585 command_query(pcmk__output_t *out, cib_t *cib)
586 {
587 int rc = pcmk_rc_ok;
588
589 xmlNode *result = NULL;
590 bool use_pattern = options.attr_pattern != NULL;
591
592
593
594
595
596
597 if (use_pattern) {
598 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
599 options.set_type, options.set_name, NULL,
600 NULL, NULL, &result);
601 } else {
602 rc = cib__get_node_attrs(out, cib, options.type, options.dest_node,
603 options.set_type, options.set_name, options.attr_id,
604 options.attr_name, NULL, &result);
605 }
606
607 if (rc == ENXIO && options.attr_default) {
608
609 const char *type = options.type;
610 const char *attr_id = options.attr_id;
611 const char *attr_name = options.attr_name;
612 const char *attr_default = options.attr_default;
613
614 out->message(out, "attribute", type, attr_id, attr_name, attr_default,
615 NULL, out->quiet, true);
616 rc = pcmk_rc_ok;
617
618 } else if (rc != pcmk_rc_ok) {
619
620
621 } else if (result->children != NULL) {
622 struct output_data_s od = { out, use_pattern, false };
623
624 pcmk__xe_foreach_child(result, NULL, output_one_attribute, &od);
625
626 if (!od.did_output) {
627 rc = ENXIO;
628 }
629
630 } else {
631 struct output_data_s od = { out, use_pattern, false };
632 output_one_attribute(result, &od);
633 }
634
635 pcmk__xml_free(result);
636 return rc;
637 }
638
639 static void
640 set_type(void)
641 {
642 if (options.type == NULL) {
643 if (options.promotion_score) {
644
645 options.type = g_strdup(PCMK_XE_STATUS);
646
647 } else if (options.dest_uname != NULL) {
648
649 options.type = g_strdup(PCMK_XE_NODES);
650
651 } else {
652
653 options.type = g_strdup(PCMK_XE_CRM_CONFIG);
654 }
655
656 } else if (pcmk__str_eq(options.type, PCMK_VALUE_REBOOT, pcmk__str_casei)) {
657 options.type = g_strdup(PCMK_XE_STATUS);
658
659 } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
660 options.type = g_strdup(PCMK_XE_NODES);
661 }
662 }
663
664 static bool
665 use_attrd(void)
666 {
667
668
669
670 return pcmk__str_eq(options.type, PCMK_XE_STATUS, pcmk__str_casei) &&
671 getenv("CIB_file") == NULL && getenv("CIB_shadow") == NULL;
672 }
673
674 static bool
675 try_ipc_update(void)
676 {
677 return use_attrd()
678 && ((options.command == attr_cmd_delete)
679 || (options.command == attr_cmd_update));
680 }
681
682 static bool
683 pattern_used_correctly(void)
684 {
685
686
687
688 switch (options.command) {
689 case attr_cmd_delete:
690 case attr_cmd_query:
691 case attr_cmd_update:
692 return true;
693 default:
694 return false;
695 }
696 }
697
698 static bool
699 delete_used_correctly(void)
700 {
701 return (options.command != attr_cmd_delete)
702 || (options.attr_name != NULL)
703 || (options.attr_pattern != NULL);
704 }
705
706 static bool
707 update_used_correctly(void)
708 {
709 return (options.command != attr_cmd_update)
710 || (options.attr_name != NULL)
711 || (options.attr_pattern != NULL);
712 }
713
714 static GOptionContext *
715 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
716 GOptionContext *context = NULL;
717
718 GOptionEntry extra_prog_entries[] = {
719 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
720 "Print only the value on stdout",
721 NULL },
722
723
724 { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
725 NULL, NULL
726 },
727
728 { NULL }
729 };
730
731 const char *description = "Examples:\n\n"
732 "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
733 "\tcrm_attribute --node myhost --name location --update office\n\n"
734 "Query the value of the 'location' node attribute for host 'myhost':\n\n"
735 "\tcrm_attribute --node myhost --name location --query\n\n"
736 "Change the value of the 'location' node attribute for host 'myhost':\n\n"
737 "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
738 "Delete the 'location' node attribute for host 'myhost':\n\n"
739 "\tcrm_attribute --node myhost --name location --delete\n\n"
740 "Query the value of the '" PCMK_OPT_CLUSTER_DELAY
741 "' cluster option:\n\n"
742 "\tcrm_attribute --type crm_config --name "
743 PCMK_OPT_CLUSTER_DELAY " --query\n\n"
744 "Query value of the '" PCMK_OPT_CLUSTER_DELAY
745 "' cluster option and print only the value:\n\n"
746 "\tcrm_attribute --type crm_config --name "
747 PCMK_OPT_CLUSTER_DELAY " --query --quiet\n\n";
748
749 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
750 pcmk__add_main_args(context, extra_prog_entries);
751 g_option_context_set_description(context, description);
752
753 pcmk__add_arg_group(context, "selections", "Selecting attributes:",
754 "Show selecting options", selecting_entries);
755 pcmk__add_arg_group(context, "command", "Commands:",
756 "Show command options", command_entries);
757 pcmk__add_arg_group(context, "additional", "Additional options:",
758 "Show additional options", addl_entries);
759 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
760 "Show deprecated options", deprecated_entries);
761
762 return context;
763 }
764
765 int
766 main(int argc, char **argv)
767 {
768 cib_t *the_cib = NULL;
769 int is_remote_node = 0;
770
771 int rc = pcmk_rc_ok;
772
773 pcmk__output_t *out = NULL;
774
775 GOptionGroup *output_group = NULL;
776 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
777 gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv");
778 GOptionContext *context = build_arg_context(args, &output_group);
779
780 pcmk__register_formats(output_group, formats);
781 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
782 exit_code = CRM_EX_USAGE;
783 goto done;
784 }
785
786 pcmk__cli_init_logging("crm_attribute", args->verbosity);
787
788 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
789 if (rc != pcmk_rc_ok) {
790 exit_code = CRM_EX_ERROR;
791 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
792 args->output_ty, pcmk_rc_str(rc));
793 goto done;
794 }
795
796 pcmk__register_lib_messages(out);
797
798 if (args->version) {
799 out->version(out, false);
800 goto done;
801 }
802
803 out->quiet = args->quiet;
804
805 if (options.command == attr_cmd_list) {
806 command_list(out);
807 goto done;
808 }
809
810 if (options.promotion_score && options.attr_name == NULL) {
811 exit_code = CRM_EX_USAGE;
812 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
813 "-p/--promotion must be called from an OCF resource agent "
814 "or with a resource ID specified");
815 goto done;
816 }
817
818 if (options.inhibit) {
819 crm_warn("Inhibiting notifications for this update");
820 cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
821 }
822
823 the_cib = cib_new();
824 rc = cib__signon_attempts(the_cib, cib_command, 5);
825 rc = pcmk_legacy2rc(rc);
826
827 if (rc != pcmk_rc_ok) {
828 exit_code = pcmk_rc2exitc(rc);
829 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
830 "Could not connect to the CIB: %s", pcmk_rc_str(rc));
831 goto done;
832 }
833
834 set_type();
835
836
837 if (!pcmk__strcase_any_of(options.type,
838 PCMK_XE_CRM_CONFIG, PCMK_XE_TICKETS, NULL)) {
839
840
841
842
843 const char *target = pcmk__node_attr_target(options.dest_uname);
844
845 if (target != NULL) {
846
847
848
849
850
851
852
853
854
855
856
857
858 if (target != (const char *) options.dest_uname) {
859 g_free(options.dest_uname);
860 options.dest_uname = g_strdup(target);
861 }
862
863 } else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) {
864 get_node_name_from_local();
865 }
866
867 if (options.dest_uname == NULL) {
868 char *node_name = NULL;
869
870 rc = pcmk__query_node_name(out, 0, &node_name, 0);
871
872 if (rc != pcmk_rc_ok) {
873 exit_code = pcmk_rc2exitc(rc);
874 free(node_name);
875 goto done;
876 }
877 options.dest_uname = g_strdup(node_name);
878 free(node_name);
879 }
880
881 rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
882 rc = pcmk_legacy2rc(rc);
883
884 if (rc != pcmk_rc_ok) {
885 exit_code = pcmk_rc2exitc(rc);
886 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
887 "Could not map name=%s to a UUID", options.dest_uname);
888 goto done;
889 }
890 }
891
892 if (!delete_used_correctly()) {
893 exit_code = CRM_EX_USAGE;
894 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
895 "Error: must specify attribute name or pattern to delete");
896 goto done;
897 }
898
899 if (!update_used_correctly()) {
900 exit_code = CRM_EX_USAGE;
901 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
902 "Error: must specify attribute name or pattern to update");
903 goto done;
904 }
905
906 if (options.attr_pattern) {
907 if (options.attr_name) {
908 exit_code = CRM_EX_USAGE;
909 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
910 "Error: --name and --pattern cannot be used at the same time");
911 goto done;
912 }
913
914 if (!pattern_used_correctly()) {
915 exit_code = CRM_EX_USAGE;
916 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
917 "Error: pattern can only be used with delete, query, or update");
918 goto done;
919 }
920
921 g_free(options.attr_name);
922 options.attr_name = options.attr_pattern;
923 options.attr_options |= pcmk__node_attr_pattern;
924 }
925
926 if (is_remote_node) {
927 options.attr_options |= pcmk__node_attr_remote;
928 }
929
930 if (pcmk__str_eq(options.set_type, PCMK_XE_UTILIZATION, pcmk__str_none)) {
931 options.attr_options |= pcmk__node_attr_utilization;
932 }
933
934 if (try_ipc_update() &&
935 (send_attrd_update(options.command, options.dest_uname, options.attr_name,
936 options.attr_value, options.set_name, NULL, options.attr_options) == pcmk_rc_ok)) {
937
938 const char *update = options.attr_value;
939
940 if (options.command == attr_cmd_delete) {
941 update = "<none>";
942 }
943 crm_info("Update %s=%s sent to the attribute manager",
944 options.attr_name, update);
945
946 } else if (options.command == attr_cmd_delete) {
947 rc = command_delete(out, the_cib);
948
949 } else if (options.command == attr_cmd_update) {
950 rc = command_update(out, the_cib, is_remote_node);
951
952 } else {
953 rc = command_query(out, the_cib);
954 }
955
956 if (rc == ENOTUNIQ) {
957 exit_code = pcmk_rc2exitc(rc);
958 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
959 "Please choose from one of the matches below and supply the 'id' with --attr-id");
960
961 } else if (rc != pcmk_rc_ok) {
962 exit_code = pcmk_rc2exitc(rc);
963 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
964 "Error performing operation: %s", pcmk_rc_str(rc));
965 }
966
967 done:
968 g_strfreev(processed_args);
969 pcmk__free_arg_context(context);
970
971 free(options.attr_default);
972 g_free(options.attr_id);
973 g_free(options.attr_name);
974 free(options.attr_value);
975 free(options.dest_node);
976 g_free(options.dest_uname);
977 g_free(options.set_name);
978 free(options.set_type);
979 g_free(options.type);
980
981 cib__clean_up_connection(&the_cib);
982
983 pcmk__output_and_clear_error(&error, out);
984
985 if (out != NULL) {
986 out->finish(out, exit_code, true, NULL);
987 pcmk__output_free(out);
988 }
989
990 pcmk__unregister_formats();
991 return crm_exit(exit_code);
992 }