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