root/tools/crm_ticket.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. find_ticket
  2. print_date
  3. print_ticket
  4. print_ticket_list
  5. find_ticket_state
  6. find_ticket_constraints
  7. dump_ticket_xml
  8. dump_constraints
  9. get_ticket_state_attr
  10. ticket_warning
  11. allow_modification
  12. modify_ticket_state
  13. delete_ticket_state
  14. main

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

/* [previous][next][first][last][top][bottom][index][help] */