This source file includes following definitions.
- attr_value_cb
- command_cb
- delete_attr_cb
- get_attr_cb
- grant_standby_cb
- set_attr_cb
- find_ticket
- print_date
- print_ticket
- print_ticket_list
- find_ticket_state
- find_ticket_constraints
- dump_ticket_xml
- dump_constraints
- get_ticket_state_attr
- ticket_warning
- allow_modification
- modify_ticket_state
- delete_ticket_state
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13
14 #include <crm/crm.h>
15
16 #include <stdio.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <libgen.h>
24
25 #include <crm/msg_xml.h>
26 #include <crm/common/xml.h>
27 #include <crm/common/ipc.h>
28 #include <crm/common/cmdline_internal.h>
29
30 #include <crm/cib.h>
31 #include <crm/cib/internal.h>
32 #include <crm/pengine/rules.h>
33 #include <crm/pengine/status.h>
34 #include <crm/pengine/internal.h>
35
36 #include <pacemaker-internal.h>
37
38 GError *error = NULL;
39
40 #define SUMMARY "Perform tasks related to cluster tickets\n\n" \
41 "Allows ticket attributes to be queried, modified and deleted."
42
43 struct {
44 gchar *attr_default;
45 gchar *attr_id;
46 char *attr_name;
47 char *attr_value;
48 gboolean force;
49 char *get_attr_name;
50 gboolean quiet;
51 gchar *set_name;
52 char ticket_cmd;
53 gchar *ticket_id;
54 gchar *xml_file;
55 } options = {
56 .ticket_cmd = 'S'
57 };
58
59 GList *attr_delete;
60 GHashTable *attr_set;
61 bool modified = false;
62 int cib_options = cib_sync_call;
63
64 #define INDENT " "
65
66 static gboolean
67 attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
68 pcmk__str_update(&options.attr_value, optarg);
69
70 if (!options.attr_name || !options.attr_value) {
71 return TRUE;
72 }
73
74 g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value));
75 pcmk__str_update(&options.attr_name, NULL);
76 pcmk__str_update(&options.attr_value, NULL);
77
78 modified = true;
79
80 return TRUE;
81 }
82
83 static gboolean
84 command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
85 if (pcmk__str_any_of(option_name, "--info", "-l", NULL)) {
86 options.ticket_cmd = 'l';
87 } else if (pcmk__str_any_of(option_name, "--details", "-L", NULL)) {
88 options.ticket_cmd = 'L';
89 } else if (pcmk__str_any_of(option_name, "--raw", "-w", NULL)) {
90 options.ticket_cmd = 'w';
91 } else if (pcmk__str_any_of(option_name, "--query-xml", "-q", NULL)) {
92 options.ticket_cmd = 'q';
93 } else if (pcmk__str_any_of(option_name, "--constraints", "-c", NULL)) {
94 options.ticket_cmd = 'c';
95 } else if (pcmk__str_any_of(option_name, "--cleanup", "-C", NULL)) {
96 options.ticket_cmd = 'C';
97 }
98
99 return TRUE;
100 }
101
102 static gboolean
103 delete_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
104 attr_delete = g_list_append(attr_delete, strdup(optarg));
105 modified = true;
106 return TRUE;
107 }
108
109 static gboolean
110 get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
111 pcmk__str_update(&options.get_attr_name, optarg);
112 options.ticket_cmd = 'G';
113 return TRUE;
114 }
115
116 static gboolean
117 grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
118 if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) {
119 g_hash_table_insert(attr_set, strdup("granted"), strdup("true"));
120 modified = true;
121 } else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) {
122 g_hash_table_insert(attr_set, strdup("granted"), strdup("false"));
123 modified = true;
124 } else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) {
125 g_hash_table_insert(attr_set, strdup("standby"), strdup("true"));
126 modified = true;
127 } else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) {
128 g_hash_table_insert(attr_set, strdup("standby"), strdup("false"));
129 modified = true;
130 }
131
132 return TRUE;
133 }
134
135 static gboolean
136 set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
137 pcmk__str_update(&options.attr_name, optarg);
138
139 if (!options.attr_name || !options.attr_value) {
140 return TRUE;
141 }
142
143 g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value));
144 pcmk__str_update(&options.attr_name, NULL);
145 pcmk__str_update(&options.attr_value, NULL);
146
147 modified = true;
148
149 return TRUE;
150 }
151
152 static GOptionEntry query_entries[] = {
153 { "info", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
154 "Display the information of ticket(s)",
155 NULL },
156
157 { "details", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
158 "Display the details of ticket(s)",
159 NULL },
160
161 { "raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
162 "Display the IDs of ticket(s)",
163 NULL },
164
165 { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
166 "Query the XML of ticket(s)",
167 NULL },
168
169 { "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
170 "Display the rsc_ticket constraints that apply to ticket(s)",
171 NULL },
172
173 { NULL }
174 };
175
176 static GOptionEntry command_entries[] = {
177 { "grant", 'g', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
178 "Grant a ticket to this cluster site",
179 NULL },
180
181 { "revoke", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
182 "Revoke a ticket from this cluster site",
183 NULL },
184
185 { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
186 "Tell this cluster site this ticket is standby",
187 NULL },
188
189 { "activate", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb,
190 "Tell this cluster site this ticket is active",
191 NULL },
192
193 { NULL }
194 };
195
196 static GOptionEntry advanced_entries[] = {
197 { "get-attr", 'G', 0, G_OPTION_ARG_CALLBACK, get_attr_cb,
198 "Display the named attribute for a ticket",
199 "ATTRIBUTE" },
200
201 { "set-attr", 'S', 0, G_OPTION_ARG_CALLBACK, set_attr_cb,
202 "Set the named attribute for a ticket",
203 "ATTRIBUTE" },
204
205 { "delete-attr", 'D', 0, G_OPTION_ARG_CALLBACK, delete_attr_cb,
206 "Delete the named attribute for a ticket",
207 "ATTRIBUTE" },
208
209 { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
210 "Delete all state of a ticket at this cluster site",
211 NULL },
212
213 { NULL}
214 };
215
216 static GOptionEntry addl_entries[] = {
217 { "attr-value", 'v', 0, G_OPTION_ARG_CALLBACK, attr_value_cb,
218 "Attribute value to use with -S",
219 "VALUE" },
220
221 { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default,
222 "(Advanced) Default attribute value to display if none is found\n"
223 INDENT "(for use with -G)",
224 "VALUE" },
225
226 { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force,
227 "(Advanced) Force the action to be performed",
228 NULL },
229
230 { "ticket", 't', 0, G_OPTION_ARG_STRING, &options.ticket_id,
231 "Ticket ID",
232 "ID" },
233
234 { "xml-file", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.xml_file,
235 NULL,
236 NULL },
237
238 { NULL }
239 };
240
241 static GOptionEntry deprecated_entries[] = {
242 { "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name,
243 "(Advanced) ID of the instance_attributes object to change",
244 "ID" },
245
246 { "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
247 "(Advanced) ID of the nvpair object to change/delete",
248 "ID" },
249
250 { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &options.quiet,
251 "Print only the value on stdout",
252 NULL },
253
254 { NULL }
255 };
256
257 static pcmk_ticket_t *
258 find_ticket(gchar *ticket_id, pcmk_scheduler_t *scheduler)
259 {
260 return g_hash_table_lookup(scheduler->tickets, ticket_id);
261 }
262
263 static void
264 print_date(time_t time)
265 {
266 int lpc = 0;
267 char date_str[26];
268
269 asctime_r(localtime(&time), date_str);
270 for (; lpc < 26; lpc++) {
271 if (date_str[lpc] == '\n') {
272 date_str[lpc] = 0;
273 }
274 }
275 fprintf(stdout, "'%s'", date_str);
276 }
277
278 static void
279 print_ticket(pcmk_ticket_t *ticket, bool raw, bool details)
280 {
281 if (raw) {
282 fprintf(stdout, "%s\n", ticket->id);
283 return;
284 }
285
286 fprintf(stdout, "%s\t%s %s",
287 ticket->id, ticket->granted ? "granted" : "revoked",
288 ticket->standby ? "[standby]" : " ");
289
290 if (details && g_hash_table_size(ticket->state) > 0) {
291 GHashTableIter iter;
292 const char *name = NULL;
293 const char *value = NULL;
294 int lpc = 0;
295
296 fprintf(stdout, " (");
297
298 g_hash_table_iter_init(&iter, ticket->state);
299 while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
300 if (lpc > 0) {
301 fprintf(stdout, ", ");
302 }
303 fprintf(stdout, "%s=", name);
304 if (pcmk__str_any_of(name, "last-granted", "expires", NULL)) {
305 long long time_ll;
306
307 pcmk__scan_ll(value, &time_ll, 0);
308 print_date((time_t) time_ll);
309 } else {
310 fprintf(stdout, "%s", value);
311 }
312 lpc++;
313 }
314
315 fprintf(stdout, ")\n");
316
317 } else {
318 if (ticket->last_granted > -1) {
319 fprintf(stdout, " last-granted=");
320 print_date(ticket->last_granted);
321 }
322 fprintf(stdout, "\n");
323 }
324
325 return;
326 }
327
328 static void
329 print_ticket_list(pcmk_scheduler_t *scheduler, bool raw, bool details)
330 {
331 GHashTableIter iter;
332 pcmk_ticket_t *ticket = NULL;
333
334 g_hash_table_iter_init(&iter, scheduler->tickets);
335
336 while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) {
337 print_ticket(ticket, raw, details);
338 }
339 }
340
341 static int
342 find_ticket_state(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_state_xml)
343 {
344 int rc = pcmk_rc_ok;
345 xmlNode *xml_search = NULL;
346
347 GString *xpath = NULL;
348
349 CRM_ASSERT(ticket_state_xml != NULL);
350 *ticket_state_xml = NULL;
351
352 xpath = g_string_sized_new(1024);
353 g_string_append(xpath,
354 "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS
355 "/" XML_CIB_TAG_TICKETS);
356
357 if (ticket_id != NULL) {
358 pcmk__g_strcat(xpath,
359 "/" XML_CIB_TAG_TICKET_STATE
360 "[@" XML_ATTR_ID "=\"", ticket_id, "\"]", NULL);
361 }
362
363 rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search,
364 cib_sync_call | cib_scope_local | cib_xpath);
365 rc = pcmk_legacy2rc(rc);
366 g_string_free(xpath, TRUE);
367
368 if (rc != pcmk_rc_ok) {
369 return rc;
370 }
371
372 crm_log_xml_debug(xml_search, "Match");
373 if (xml_search->children != NULL) {
374 if (ticket_id) {
375 fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id);
376 }
377 *ticket_state_xml = xml_search;
378 } else {
379 *ticket_state_xml = xml_search;
380 }
381 return rc;
382 }
383
384 static int
385 find_ticket_constraints(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_cons_xml)
386 {
387 int rc = pcmk_rc_ok;
388 xmlNode *xml_search = NULL;
389
390 GString *xpath = NULL;
391 const char *xpath_base = NULL;
392
393 CRM_ASSERT(ticket_cons_xml != NULL);
394 *ticket_cons_xml = NULL;
395
396 xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_CONSTRAINTS);
397 if (xpath_base == NULL) {
398 crm_err(XML_CIB_TAG_CONSTRAINTS " CIB element not known (bug?)");
399 return -ENOMSG;
400 }
401
402 xpath = g_string_sized_new(1024);
403 pcmk__g_strcat(xpath, xpath_base, "/" XML_CONS_TAG_RSC_TICKET, NULL);
404
405 if (ticket_id != NULL) {
406 pcmk__g_strcat(xpath,
407 "[@" XML_TICKET_ATTR_TICKET "=\"", ticket_id, "\"]",
408 NULL);
409 }
410
411 rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search,
412 cib_sync_call | cib_scope_local | cib_xpath);
413 rc = pcmk_legacy2rc(rc);
414 g_string_free(xpath, TRUE);
415
416 if (rc != pcmk_rc_ok) {
417 return rc;
418 }
419
420 crm_log_xml_debug(xml_search, "Match");
421 *ticket_cons_xml = xml_search;
422
423 return rc;
424 }
425
426 static int
427 dump_ticket_xml(cib_t * the_cib, gchar *ticket_id)
428 {
429 int rc = pcmk_rc_ok;
430 xmlNode *state_xml = NULL;
431
432 rc = find_ticket_state(the_cib, ticket_id, &state_xml);
433
434 if (state_xml == NULL) {
435 return rc;
436 }
437
438 fprintf(stdout, "State XML:\n");
439 if (state_xml) {
440 char *state_xml_str = NULL;
441
442 state_xml_str = dump_xml_formatted(state_xml);
443 fprintf(stdout, "\n%s", state_xml_str);
444 free_xml(state_xml);
445 free(state_xml_str);
446 }
447
448 return rc;
449 }
450
451 static int
452 dump_constraints(cib_t * the_cib, gchar *ticket_id)
453 {
454 int rc = pcmk_rc_ok;
455 xmlNode *cons_xml = NULL;
456 char *cons_xml_str = NULL;
457
458 rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml);
459
460 if (cons_xml == NULL) {
461 return rc;
462 }
463
464 cons_xml_str = dump_xml_formatted(cons_xml);
465 fprintf(stdout, "Constraints XML:\n\n%s", cons_xml_str);
466 free_xml(cons_xml);
467 free(cons_xml_str);
468
469 return rc;
470 }
471
472 static int
473 get_ticket_state_attr(gchar *ticket_id, const char *attr_name, const char **attr_value,
474 pcmk_scheduler_t *scheduler)
475 {
476 pcmk_ticket_t *ticket = NULL;
477
478 CRM_ASSERT(attr_value != NULL);
479 *attr_value = NULL;
480
481 ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
482 if (ticket == NULL) {
483 return ENXIO;
484 }
485
486 *attr_value = g_hash_table_lookup(ticket->state, attr_name);
487 if (*attr_value == NULL) {
488 return ENXIO;
489 }
490
491 return pcmk_rc_ok;
492 }
493
494 static void
495 ticket_warning(gchar *ticket_id, const char *action)
496 {
497 GString *warning = g_string_sized_new(1024);
498 const char *word = NULL;
499
500 CRM_ASSERT(action != NULL);
501
502 if (strcmp(action, "grant") == 0) {
503 pcmk__g_strcat(warning,
504 "This command cannot help you verify whether '",
505 ticket_id,
506 "' has been already granted elsewhere.\n", NULL);
507 word = "to";
508
509 } else {
510 pcmk__g_strcat(warning,
511 "Revoking '", ticket_id, "' can trigger the specified "
512 "'loss-policy'(s) relating to '", ticket_id, "'.\n\n"
513 "You can check that with:\n"
514 "crm_ticket --ticket ", ticket_id, " --constraints\n\n"
515 "Otherwise before revoking '", ticket_id, "', "
516 "you may want to make '", ticket_id, "' "
517 "standby with:\n"
518 "crm_ticket --ticket ", ticket_id, " --standby\n\n",
519 NULL);
520 word = "from";
521 }
522
523 pcmk__g_strcat(warning,
524 "If you really want to ", action, " '", ticket_id, "' ",
525 word, " this site now, and you know what you are doing,\n"
526 "please specify --force.", NULL);
527
528 fprintf(stdout, "%s\n", (const char *) warning->str);
529
530 g_string_free(warning, TRUE);
531 }
532
533 static bool
534 allow_modification(gchar *ticket_id)
535 {
536 const char *value = NULL;
537 GList *list_iter = NULL;
538
539 if (options.force) {
540 return true;
541 }
542
543 if (g_hash_table_lookup_extended(attr_set, "granted", NULL, (gpointer *) & value)) {
544 if (crm_is_true(value)) {
545 ticket_warning(ticket_id, "grant");
546 return false;
547
548 } else {
549 ticket_warning(ticket_id, "revoke");
550 return false;
551 }
552 }
553
554 for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) {
555 const char *key = (const char *)list_iter->data;
556
557 if (pcmk__str_eq(key, "granted", pcmk__str_casei)) {
558 ticket_warning(ticket_id, "revoke");
559 return false;
560 }
561 }
562
563 return true;
564 }
565
566 static int
567 modify_ticket_state(gchar *ticket_id, cib_t *cib, pcmk_scheduler_t *scheduler)
568 {
569 int rc = pcmk_rc_ok;
570 xmlNode *xml_top = NULL;
571 xmlNode *ticket_state_xml = NULL;
572 bool found = false;
573
574 GList *list_iter = NULL;
575 GHashTableIter hash_iter;
576
577 char *key = NULL;
578 char *value = NULL;
579
580 pcmk_ticket_t *ticket = NULL;
581
582 rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
583 if (rc == pcmk_rc_ok) {
584 crm_debug("Found a match state for ticket: id=%s", ticket_id);
585 xml_top = ticket_state_xml;
586 found = true;
587
588 } else if (rc != ENXIO) {
589 return rc;
590
591 } else if (g_hash_table_size(attr_set) == 0){
592 return pcmk_rc_ok;
593
594 } else {
595 xmlNode *xml_obj = NULL;
596
597 xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
598 xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
599 ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE);
600 crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id);
601 }
602
603 for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) {
604 const char *key = (const char *)list_iter->data;
605 xml_remove_prop(ticket_state_xml, key);
606 }
607
608 ticket = find_ticket(ticket_id, scheduler);
609
610 g_hash_table_iter_init(&hash_iter, attr_set);
611 while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) {
612 crm_xml_add(ticket_state_xml, key, value);
613
614 if (pcmk__str_eq(key, "granted", pcmk__str_casei)
615 && (ticket == NULL || ticket->granted == FALSE)
616 && crm_is_true(value)) {
617
618 char *now = pcmk__ttoa(time(NULL));
619
620 crm_xml_add(ticket_state_xml, "last-granted", now);
621 free(now);
622 }
623 }
624
625 if (found && (attr_delete != NULL)) {
626 crm_log_xml_debug(xml_top, "Replace");
627 rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
628 rc = pcmk_legacy2rc(rc);
629
630 } else {
631 crm_log_xml_debug(xml_top, "Update");
632 rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options);
633 rc = pcmk_legacy2rc(rc);
634 }
635
636 free_xml(xml_top);
637 return rc;
638 }
639
640 static int
641 delete_ticket_state(gchar *ticket_id, cib_t * cib)
642 {
643 xmlNode *ticket_state_xml = NULL;
644
645 int rc = pcmk_rc_ok;
646
647 rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
648
649 if (rc == ENXIO) {
650 return pcmk_rc_ok;
651
652 } else if (rc != pcmk_rc_ok) {
653 return rc;
654 }
655
656 crm_log_xml_debug(ticket_state_xml, "Delete");
657
658 rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
659 rc = pcmk_legacy2rc(rc);
660
661 if (rc == pcmk_rc_ok) {
662 fprintf(stdout, "Cleaned up %s\n", ticket_id);
663 }
664
665 free_xml(ticket_state_xml);
666 return rc;
667 }
668
669 static GOptionContext *
670 build_arg_context(pcmk__common_args_t *args) {
671 GOptionContext *context = NULL;
672
673 const char *description = "Examples:\n\n"
674 "Display the info of tickets:\n\n"
675 "\tcrm_ticket --info\n\n"
676 "Display the detailed info of tickets:\n\n"
677 "\tcrm_ticket --details\n\n"
678 "Display the XML of 'ticketA':\n\n"
679 "\tcrm_ticket --ticket ticketA --query-xml\n\n"
680 "Display the rsc_ticket constraints that apply to 'ticketA':\n\n"
681 "\tcrm_ticket --ticket ticketA --constraints\n\n"
682 "Grant 'ticketA' to this cluster site:\n\n"
683 "\tcrm_ticket --ticket ticketA --grant\n\n"
684 "Revoke 'ticketA' from this cluster site:\n\n"
685 "\tcrm_ticket --ticket ticketA --revoke\n\n"
686 "Make 'ticketA' standby (the cluster site will treat a granted\n"
687 "'ticketA' as 'standby', and the dependent resources will be\n"
688 "stopped or demoted gracefully without triggering loss-policies):\n\n"
689 "\tcrm_ticket --ticket ticketA --standby\n\n"
690 "Activate 'ticketA' from being standby:\n\n"
691 "\tcrm_ticket --ticket ticketA --activate\n\n"
692 "Get the value of the 'granted' attribute for 'ticketA':\n\n"
693 "\tcrm_ticket --ticket ticketA --get-attr granted\n\n"
694 "Set the value of the 'standby' attribute for 'ticketA':\n\n"
695 "\tcrm_ticket --ticket ticketA --set-attr standby --attr-value true\n\n"
696 "Delete the 'granted' attribute for 'ticketA':\n\n"
697 "\tcrm_ticket --ticket ticketA --delete-attr granted\n\n"
698 "Erase the operation history of 'ticketA' at this cluster site,\n"
699 "causing the cluster site to 'forget' the existing ticket state:\n\n"
700 "\tcrm_ticket --ticket ticketA --cleanup\n\n";
701
702 context = pcmk__build_arg_context(args, NULL, NULL, NULL);
703 g_option_context_set_description(context, description);
704
705 pcmk__add_arg_group(context, "queries", "Queries:",
706 "Show queries", query_entries);
707 pcmk__add_arg_group(context, "commands", "Commands:",
708 "Show command options", command_entries);
709 pcmk__add_arg_group(context, "advanced", "Advanced Options:",
710 "Show advanced options", advanced_entries);
711 pcmk__add_arg_group(context, "additional", "Additional Options:",
712 "Show additional options", addl_entries);
713 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
714 "Show deprecated options", deprecated_entries);
715
716 return context;
717 }
718
719 int
720 main(int argc, char **argv)
721 {
722 pcmk_scheduler_t *scheduler = NULL;
723 xmlNode *cib_xml_copy = NULL;
724
725 cib_t *cib_conn = NULL;
726 crm_exit_t exit_code = CRM_EX_OK;
727 int rc = pcmk_rc_ok;
728
729 pcmk__common_args_t *args = NULL;
730 GOptionContext *context = NULL;
731 gchar **processed_args = NULL;
732
733 attr_set = pcmk__strkey_table(free, free);
734 attr_delete = NULL;
735
736 args = pcmk__new_common_args(SUMMARY);
737 context = build_arg_context(args);
738 processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS");
739
740 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
741 exit_code = CRM_EX_USAGE;
742 goto done;
743 }
744
745 pcmk__cli_init_logging("crm_ticket", args->verbosity);
746
747 if (args->version) {
748 g_strfreev(processed_args);
749 pcmk__free_arg_context(context);
750
751 pcmk__cli_help('v');
752 }
753
754 scheduler = pe_new_working_set();
755 if (scheduler == NULL) {
756 rc = errno;
757 exit_code = pcmk_rc2exitc(rc);
758 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
759 "Could not allocate scheduler data: %s", pcmk_rc_str(rc));
760 goto done;
761 }
762 pe__set_working_set_flags(scheduler,
763 pcmk_sched_no_counts|pcmk_sched_no_compat);
764
765 cib_conn = cib_new();
766 if (cib_conn == NULL) {
767 exit_code = CRM_EX_DISCONNECT;
768 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB manager");
769 goto done;
770 }
771
772 rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
773 rc = pcmk_legacy2rc(rc);
774
775 if (rc != pcmk_rc_ok) {
776 exit_code = pcmk_rc2exitc(rc);
777 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB: %s",
778 pcmk_rc_str(rc));
779 goto done;
780 }
781
782 if (options.xml_file != NULL) {
783 cib_xml_copy = filename2xml(options.xml_file);
784
785 } else {
786 rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call);
787 rc = pcmk_legacy2rc(rc);
788
789 if (rc != pcmk_rc_ok) {
790 exit_code = pcmk_rc2exitc(rc);
791 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not get local CIB: %s",
792 pcmk_rc_str(rc));
793 goto done;
794 }
795 }
796
797 if (!cli_config_update(&cib_xml_copy, NULL, FALSE)) {
798 exit_code = CRM_EX_CONFIG;
799 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
800 "Could not update local CIB to latest schema version");
801 goto done;
802 }
803
804 scheduler->input = cib_xml_copy;
805 scheduler->now = crm_time_new(NULL);
806
807 cluster_status(scheduler);
808
809
810
811 pcmk__unpack_constraints(scheduler);
812
813 if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') {
814 bool raw = false;
815 bool details = false;
816
817 if (options.ticket_cmd == 'L') {
818 details = true;
819 } else if (options.ticket_cmd == 'w') {
820 raw = true;
821 }
822
823 if (options.ticket_id) {
824 pcmk_ticket_t *ticket = find_ticket(options.ticket_id, scheduler);
825
826 if (ticket == NULL) {
827 exit_code = CRM_EX_NOSUCH;
828 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
829 "No such ticket '%s'", options.ticket_id);
830 goto done;
831 }
832 print_ticket(ticket, raw, details);
833
834 } else {
835 print_ticket_list(scheduler, raw, details);
836 }
837
838 } else if (options.ticket_cmd == 'q') {
839 rc = dump_ticket_xml(cib_conn, options.ticket_id);
840 exit_code = pcmk_rc2exitc(rc);
841
842 if (rc != pcmk_rc_ok) {
843 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
844 "Could not query ticket XML: %s", pcmk_rc_str(rc));
845 }
846
847 } else if (options.ticket_cmd == 'c') {
848 rc = dump_constraints(cib_conn, options.ticket_id);
849 exit_code = pcmk_rc2exitc(rc);
850
851 if (rc != pcmk_rc_ok) {
852 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
853 "Could not show ticket constraints: %s", pcmk_rc_str(rc));
854 }
855
856 } else if (options.ticket_cmd == 'G') {
857 const char *value = NULL;
858
859 if (options.ticket_id == NULL) {
860 exit_code = CRM_EX_NOSUCH;
861 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
862 "Must supply ticket ID with -t");
863 goto done;
864 }
865
866 rc = get_ticket_state_attr(options.ticket_id, options.get_attr_name,
867 &value, scheduler);
868 if (rc == pcmk_rc_ok) {
869 fprintf(stdout, "%s\n", value);
870 } else if (rc == ENXIO && options.attr_default) {
871 fprintf(stdout, "%s\n", options.attr_default);
872 rc = pcmk_rc_ok;
873 }
874 exit_code = pcmk_rc2exitc(rc);
875
876 } else if (options.ticket_cmd == 'C') {
877 if (options.ticket_id == NULL) {
878 exit_code = CRM_EX_USAGE;
879 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
880 "Must supply ticket ID with -t");
881 goto done;
882 }
883
884 if (options.force == FALSE) {
885 pcmk_ticket_t *ticket = NULL;
886
887 ticket = find_ticket(options.ticket_id, scheduler);
888 if (ticket == NULL) {
889 exit_code = CRM_EX_NOSUCH;
890 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
891 "No such ticket '%s'", options.ticket_id);
892 goto done;
893 }
894
895 if (ticket->granted) {
896 ticket_warning(options.ticket_id, "revoke");
897 exit_code = CRM_EX_INSUFFICIENT_PRIV;
898 goto done;
899 }
900 }
901
902 rc = delete_ticket_state(options.ticket_id, cib_conn);
903 exit_code = pcmk_rc2exitc(rc);
904
905 if (rc != pcmk_rc_ok) {
906 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
907 "Could not clean up ticket: %s", pcmk_rc_str(rc));
908 }
909
910 } else if (modified) {
911 if (options.ticket_id == NULL) {
912 exit_code = CRM_EX_USAGE;
913 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
914 "Must supply ticket ID with -t");
915 goto done;
916 }
917
918 if (options.attr_value
919 && (pcmk__str_empty(options.attr_name))) {
920 exit_code = CRM_EX_USAGE;
921 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
922 "Must supply attribute name with -S for -v %s", options.attr_value);
923 goto done;
924 }
925
926 if (options.attr_name
927 && (pcmk__str_empty(options.attr_value))) {
928 exit_code = CRM_EX_USAGE;
929 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
930 "Must supply attribute value with -v for -S %s", options.attr_value);
931 goto done;
932 }
933
934 if (!allow_modification(options.ticket_id)) {
935 exit_code = CRM_EX_INSUFFICIENT_PRIV;
936 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
937 "Ticket modification not allowed");
938 goto done;
939 }
940
941 rc = modify_ticket_state(options.ticket_id, cib_conn, scheduler);
942 exit_code = pcmk_rc2exitc(rc);
943
944 if (rc != pcmk_rc_ok) {
945 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
946 "Could not modify ticket: %s", pcmk_rc_str(rc));
947 }
948
949 } else if (options.ticket_cmd == 'S') {
950
951
952
953
954 if (pcmk__str_empty(options.attr_name)) {
955
956 exit_code = CRM_EX_USAGE;
957 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Must supply a command");
958 goto done;
959 }
960
961 if (options.ticket_id == NULL) {
962 exit_code = CRM_EX_USAGE;
963 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
964 "Must supply ticket ID with -t");
965 goto done;
966 }
967
968 if (pcmk__str_empty(options.attr_value)) {
969 exit_code = CRM_EX_USAGE;
970 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
971 "Must supply value with -v for -S %s", options.attr_name);
972 goto done;
973 }
974
975 } else {
976 exit_code = CRM_EX_USAGE;
977 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
978 "Unknown command: %c", options.ticket_cmd);
979 }
980
981 done:
982 if (attr_set) {
983 g_hash_table_destroy(attr_set);
984 }
985 attr_set = NULL;
986
987 if (attr_delete) {
988 g_list_free_full(attr_delete, free);
989 }
990 attr_delete = NULL;
991
992 pe_free_working_set(scheduler);
993 scheduler = NULL;
994
995 cib__clean_up_connection(&cib_conn);
996
997 g_strfreev(processed_args);
998 pcmk__free_arg_context(context);
999 g_free(options.attr_default);
1000 g_free(options.attr_id);
1001 free(options.attr_name);
1002 free(options.attr_value);
1003 free(options.get_attr_name);
1004 g_free(options.set_name);
1005 g_free(options.ticket_id);
1006 g_free(options.xml_file);
1007
1008 pcmk__output_and_clear_error(&error, NULL);
1009
1010 crm_exit(exit_code);
1011 }