This source file includes following definitions.
- delete_cb
- promotion_cb
- update_cb
- utilization_cb
- value_cb
- 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/msg_xml.h>
26 #include <crm/common/xml.h>
27 #include <crm/common/ipc.h>
28 #include <crm/common/util.h>
29 #include <crm/cluster.h>
30
31 #include <crm/cib.h>
32 #include <crm/cib/internal.h>
33 #include <crm/common/attrd_internal.h>
34 #include <crm/common/cmdline_internal.h>
35 #include <sys/utsname.h>
36
37 #define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
38
39 crm_exit_t exit_code = CRM_EX_OK;
40 uint64_t cib_opts = cib_sync_call;
41
42 struct {
43 char command;
44 gchar *attr_default;
45 gchar *attr_id;
46 gchar *attr_name;
47 gchar *attr_pattern;
48 char *attr_value;
49 char *dest_node;
50 gchar *dest_uname;
51 gboolean inhibit;
52 gchar *set_name;
53 char *set_type;
54 gchar *type;
55 gboolean promotion_score;
56 } options = {
57 .command = 'G',
58 .promotion_score = FALSE
59 };
60
61 gboolean BE_QUIET = FALSE;
62
63 #define INDENT " "
64
65 static gboolean
66 delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
67 options.command = 'D';
68
69 if (options.attr_value) {
70 free(options.attr_value);
71 }
72
73 options.attr_value = NULL;
74 return TRUE;
75 }
76
77 static gboolean
78 promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
79 char *score_name = NULL;
80
81 options.promotion_score = TRUE;
82
83 if (options.attr_name) {
84 g_free(options.attr_name);
85 }
86
87 score_name = pcmk_promotion_score_name(optarg);
88 if (score_name != NULL) {
89 options.attr_name = g_strdup(score_name);
90 free(score_name);
91 } else {
92 options.attr_name = NULL;
93 }
94
95 return TRUE;
96 }
97
98 static gboolean
99 update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
100 options.command = 'v';
101
102 if (options.attr_value) {
103 free(options.attr_value);
104 }
105
106 options.attr_value = strdup(optarg);
107 return TRUE;
108 }
109
110 static gboolean
111 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
112 if (options.type) {
113 g_free(options.type);
114 }
115
116 options.type = g_strdup(XML_CIB_TAG_NODES);
117
118 if (options.set_type) {
119 free(options.set_type);
120 }
121
122 options.set_type = strdup(XML_TAG_UTILIZATION);
123 return TRUE;
124 }
125
126 static gboolean
127 value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
128 options.command = 'G';
129
130 if (options.attr_value) {
131 free(options.attr_value);
132 }
133
134 options.attr_value = NULL;
135 return TRUE;
136 }
137
138 static GOptionEntry selecting_entries[] = {
139 { "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
140 "(Advanced) Operate on instance of specified attribute with this\n"
141 INDENT "XML ID",
142 "XML_ID"
143 },
144
145 { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
146 "Operate on attribute or option with this name",
147 "NAME"
148 },
149
150 { "pattern", 'P', 0, G_OPTION_ARG_STRING, &options.attr_pattern,
151 "Operate on all attributes matching this pattern\n"
152 INDENT "(with -v/-D and -l reboot)",
153 "PATTERN"
154 },
155
156 { "promotion", 'p', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, promotion_cb,
157 "Operate on node attribute used as promotion score for specified\n"
158 INDENT "resource, or resource given in OCF_RESOURCE_INSTANCE environment\n"
159 INDENT "variable if none is specified; this also defaults -l/--lifetime\n"
160 INDENT "to reboot (normally invoked from an OCF resource agent)",
161 "RESOURCE"
162 },
163
164 { "set-name", 's', 0, G_OPTION_ARG_STRING, &options.set_name,
165 "(Advanced) Operate on instance of specified attribute that is\n"
166 INDENT "within set with this XML ID",
167 "NAME"
168 },
169
170 { NULL }
171 };
172
173 static GOptionEntry command_entries[] = {
174 { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
175 "Delete the attribute/option",
176 NULL
177 },
178
179 { "query", 'G', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb,
180 "Query the current value of the attribute/option",
181 NULL
182 },
183
184 { "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
185 "Update the value of the attribute/option",
186 "VALUE"
187 },
188
189 { NULL }
190 };
191
192 static GOptionEntry addl_entries[] = {
193 { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
194 "(Advanced) Default value to display if none is found in configuration",
195 "VALUE"
196 },
197
198 { "lifetime", 'l', 0, G_OPTION_ARG_STRING, &options.type,
199 "Lifetime of the node attribute.\n"
200 INDENT "Valid values: reboot, forever",
201 "LIFETIME"
202 },
203
204 { "node", 'N', 0, G_OPTION_ARG_STRING, &options.dest_uname,
205 "Set a node attribute for named node (instead of a cluster option).\n"
206 INDENT "See also: -l",
207 "NODE"
208 },
209
210 { "type", 't', 0, G_OPTION_ARG_STRING, &options.type,
211 "Which part of the configuration to update/delete/query the option in.\n"
212 INDENT "Valid values: crm_config, rsc_defaults, op_defaults, tickets",
213 "SECTION"
214 },
215
216 { "utilization", 'z', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
217 "Set an utilization attribute for the node.",
218 NULL
219 },
220
221 { "inhibit-policy-engine", '!', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.inhibit,
222 NULL, NULL
223 },
224
225 { NULL }
226 };
227
228 static GOptionEntry deprecated_entries[] = {
229 { "attr-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_id,
230 NULL, NULL
231 },
232
233 { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name,
234 NULL, NULL
235 },
236
237 { "attr-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, update_cb,
238 NULL, NULL
239 },
240
241 { "delete-attr", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, delete_cb,
242 NULL, NULL
243 },
244
245 { "get-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, value_cb,
246 NULL, NULL
247 },
248
249 { "node-uname", 'U', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.dest_uname,
250 NULL, NULL
251 },
252
253 { NULL }
254 };
255
256 static GOptionContext *
257 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
258 GOptionContext *context = NULL;
259
260 GOptionEntry extra_prog_entries[] = {
261 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
262 "Print only the value on stdout",
263 NULL },
264
265 { "quiet", 'Q', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &(args->quiet),
266 NULL, NULL
267 },
268
269 { NULL }
270 };
271
272 const char *description = "Examples:\n\n"
273 "Add new node attribute called 'location' with the value of 'office' for host 'myhost':\n\n"
274 "\tcrm_attribute --node myhost --name location --update office\n\n"
275 "Query the value of the 'location' node attribute for host 'myhost':\n\n"
276 "\tcrm_attribute --node myhost --name location --query\n\n"
277 "Change the value of the 'location' node attribute for host 'myhost':\n\n"
278 "\tcrm_attribute --node myhost --name location --update backoffice\n\n"
279 "Delete the 'location' node attribute for host 'myhost':\n\n"
280 "\tcrm_attribute --node myhost --name location --delete\n\n"
281 "Query the value of the 'cluster-delay' cluster option:\n\n"
282 "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n"
283 "Query value of the 'cluster-delay' cluster option and print only the value:\n\n"
284 "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n";
285
286 context = pcmk__build_arg_context(args, NULL, group, NULL);
287 pcmk__add_main_args(context, extra_prog_entries);
288 g_option_context_set_description(context, description);
289
290 pcmk__add_arg_group(context, "selections", "Selecting attributes:",
291 "Show selecting options", selecting_entries);
292 pcmk__add_arg_group(context, "command", "Commands:",
293 "Show command options", command_entries);
294 pcmk__add_arg_group(context, "additional", "Additional options:",
295 "Show additional options", addl_entries);
296 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
297 "Show deprecated options", deprecated_entries);
298
299 return context;
300 }
301
302 int
303 main(int argc, char **argv)
304 {
305 cib_t *the_cib = NULL;
306 int is_remote_node = 0;
307 bool try_attrd = true;
308 int attrd_opts = pcmk__node_attr_none;
309
310 int rc = pcmk_ok;
311 GError *error = NULL;
312
313 GOptionGroup *output_group = NULL;
314 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
315 gchar **processed_args = pcmk__cmdline_preproc(argv, "DGNPdilnpstv");
316 GOptionContext *context = build_arg_context(args, &output_group);
317
318 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
319 exit_code = CRM_EX_USAGE;
320 goto done;
321 }
322
323 pcmk__cli_init_logging("crm_attribute", 0);
324
325 if (args->version) {
326 g_strfreev(processed_args);
327 pcmk__free_arg_context(context);
328
329 pcmk__cli_help('v', CRM_EX_USAGE);
330 }
331
332 if (options.promotion_score && options.attr_name == NULL) {
333 fprintf(stderr, "-p/--promotion must be called from an "
334 " OCF resource agent or with a resource ID "
335 " specified\n\n");
336 exit_code = CRM_EX_USAGE;
337 goto done;
338 }
339
340 if (options.inhibit) {
341 crm_warn("Inhibiting notifications for this update");
342 cib__set_call_options(cib_opts, crm_system_name, cib_inhibit_notify);
343 }
344
345 if (args->quiet) {
346 BE_QUIET = TRUE;
347 }
348
349 the_cib = cib_new();
350 rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
351
352 if (rc != pcmk_ok) {
353 fprintf(stderr, "Could not connect to the CIB: %s\n",
354 pcmk_strerror(rc));
355 exit_code = crm_errno2exit(rc);
356 goto done;
357 }
358
359
360 if (options.type == NULL) {
361 if (options.promotion_score) {
362
363 options.type = g_strdup(XML_CIB_TAG_STATUS);
364
365 } else if (options.dest_uname != NULL) {
366
367 options.type = g_strdup(XML_CIB_TAG_NODES);
368
369 } else {
370
371 options.type = g_strdup(XML_CIB_TAG_CRMCONFIG);
372 }
373 } else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
374 options.type = g_strdup(XML_CIB_TAG_STATUS);
375
376 } else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
377 options.type = g_strdup(XML_CIB_TAG_NODES);
378 }
379
380
381 if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS,
382 NULL)) {
383
384
385
386
387 const char *target = pcmk__node_attr_target(options.dest_uname);
388
389 if (target != NULL) {
390 g_free(options.dest_uname);
391 options.dest_uname = g_strdup(target);
392 }
393
394 if (options.dest_uname == NULL) {
395 options.dest_uname = g_strdup(get_local_node_name());
396 }
397
398 rc = query_node_uuid(the_cib, options.dest_uname, &options.dest_node, &is_remote_node);
399 if (pcmk_ok != rc) {
400 fprintf(stderr, "Could not map name=%s to a UUID\n", options.dest_uname);
401 exit_code = crm_errno2exit(rc);
402 goto done;
403 }
404 }
405
406 if ((options.command == 'D') && (options.attr_name == NULL) && (options.attr_pattern == NULL)) {
407 fprintf(stderr, "Error: must specify attribute name or pattern to delete\n");
408 exit_code = CRM_EX_USAGE;
409 goto done;
410 }
411
412 if (options.attr_pattern) {
413 if (((options.command != 'v') && (options.command != 'D'))
414 || !pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
415
416 fprintf(stderr, "Error: pattern can only be used with till-reboot update or delete\n");
417 exit_code = CRM_EX_USAGE;
418 goto done;
419 }
420 options.command = 'u';
421 g_free(options.attr_name);
422 options.attr_name = options.attr_pattern;
423 }
424
425
426 try_attrd = pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei);
427
428
429 if (getenv("CIB_file") || getenv("CIB_shadow")) {
430 try_attrd = FALSE;
431 }
432
433 if (is_remote_node) {
434 attrd_opts = pcmk__node_attr_remote;
435 }
436 if (((options.command == 'v') || (options.command == 'D') || (options.command == 'u')) && try_attrd
437 && (pcmk__node_attr_request(NULL, options.command, options.dest_uname, options.attr_name,
438 options.attr_value, options.type, options.set_name, NULL, NULL,
439 attrd_opts) == pcmk_rc_ok)) {
440 crm_info("Update %s=%s sent via pacemaker-attrd",
441 options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value));
442
443 } else if (options.command == 'D') {
444 rc = delete_attr_delegate(the_cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name,
445 options.attr_id, options.attr_name, options.attr_value, TRUE, NULL);
446
447 if (rc == -ENXIO) {
448
449
450
451
452 rc = pcmk_ok;
453 }
454
455 } else if (options.command == 'v') {
456 CRM_LOG_ASSERT(options.type != NULL);
457 CRM_LOG_ASSERT(options.attr_name != NULL);
458 CRM_LOG_ASSERT(options.attr_value != NULL);
459
460 rc = update_attr_delegate(the_cib, cib_opts, options.type, options.dest_node, options.set_type, options.set_name,
461 options.attr_id, options.attr_name, options.attr_value, TRUE, NULL, is_remote_node ? "remote" : NULL);
462
463 } else {
464
465 char *read_value = NULL;
466
467 rc = read_attr_delegate(the_cib, options.type, options.dest_node, options.set_type, options.set_name,
468 options.attr_id, options.attr_name, &read_value, TRUE, NULL);
469
470 if (rc == -ENXIO && options.attr_default) {
471 read_value = strdup(options.attr_default);
472 rc = pcmk_ok;
473 }
474
475 crm_info("Read %s=%s %s%s",
476 options.attr_name, crm_str(read_value), options.set_name ? "in " : "", options.set_name ? options.set_name : "");
477
478 if (rc == -ENOTUNIQ) {
479
480 rc = pcmk_ok;
481
482 } else if (BE_QUIET == FALSE) {
483 fprintf(stdout, "%s%s %s%s %s%s value=%s\n",
484 options.type ? "scope=" : "", options.type ? options.type : "",
485 options.attr_id ? "id=" : "", options.attr_id ? options.attr_id : "",
486 options.attr_name ? "name=" : "", options.attr_name ? options.attr_name : "",
487 read_value ? read_value : "(null)");
488
489 } else if (read_value != NULL) {
490 fprintf(stdout, "%s\n", read_value);
491 }
492 free(read_value);
493 }
494
495 if (rc == -ENOTUNIQ) {
496 printf("Please choose from one of the matches above and supply the 'id' with --attr-id\n");
497 exit_code = crm_errno2exit(rc);
498
499 } else if (rc != pcmk_ok) {
500 fprintf(stderr, "Error performing operation: %s\n", pcmk_strerror(rc));
501 exit_code = crm_errno2exit(rc);
502 }
503
504 done:
505 g_strfreev(processed_args);
506 pcmk__free_arg_context(context);
507
508 free(options.attr_default);
509 g_free(options.attr_id);
510 g_free(options.attr_name);
511 free(options.attr_value);
512 free(options.dest_node);
513 g_free(options.dest_uname);
514 g_free(options.set_name);
515 free(options.set_type);
516 g_free(options.type);
517
518 if (the_cib) {
519 the_cib->cmds->signoff(the_cib);
520 cib_delete(the_cib);
521 }
522
523 pcmk__output_and_clear_error(error, NULL);
524 return crm_exit(exit_code);
525 }