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

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