diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:53:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:53:20 +0000 |
commit | e5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch) | |
tree | a6716c9275b4b413f6c9194798b34b91affb3cc7 /tools/crm_ticket.c | |
parent | Initial commit. (diff) | |
download | pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip |
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/crm_ticket.c')
-rw-r--r-- | tools/crm_ticket.c | 1007 |
1 files changed, 1007 insertions, 0 deletions
diff --git a/tools/crm_ticket.c b/tools/crm_ticket.c new file mode 100644 index 0000000..c451e8a --- /dev/null +++ b/tools/crm_ticket.c @@ -0,0 +1,1007 @@ +/* + * Copyright 2012-2023 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include <crm_internal.h> + +#include <sys/param.h> + +#include <crm/crm.h> + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> + +#include <crm/msg_xml.h> +#include <crm/common/xml.h> +#include <crm/common/ipc.h> +#include <crm/common/cmdline_internal.h> + +#include <crm/cib.h> +#include <crm/cib/internal.h> +#include <crm/pengine/rules.h> +#include <crm/pengine/status.h> + +#include <pacemaker-internal.h> + +GError *error = NULL; + +#define SUMMARY "Perform tasks related to cluster tickets\n\n" \ + "Allows ticket attributes to be queried, modified and deleted." + +struct { + gchar *attr_default; + gchar *attr_id; + char *attr_name; + char *attr_value; + gboolean force; + char *get_attr_name; + gboolean quiet; + gchar *set_name; + char ticket_cmd; + gchar *ticket_id; + gchar *xml_file; +} options = { + .ticket_cmd = 'S' +}; + +GList *attr_delete; +GHashTable *attr_set; +bool modified = false; +int cib_options = cib_sync_call; + +#define INDENT " " + +static gboolean +attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.attr_value, optarg); + + if (!options.attr_name || !options.attr_value) { + return TRUE; + } + + g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value)); + pcmk__str_update(&options.attr_name, NULL); + pcmk__str_update(&options.attr_value, NULL); + + modified = true; + + return TRUE; +} + +static gboolean +command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + if (pcmk__str_any_of(option_name, "--info", "-l", NULL)) { + options.ticket_cmd = 'l'; + } else if (pcmk__str_any_of(option_name, "--details", "-L", NULL)) { + options.ticket_cmd = 'L'; + } else if (pcmk__str_any_of(option_name, "--raw", "-w", NULL)) { + options.ticket_cmd = 'w'; + } else if (pcmk__str_any_of(option_name, "--query-xml", "-q", NULL)) { + options.ticket_cmd = 'q'; + } else if (pcmk__str_any_of(option_name, "--constraints", "-c", NULL)) { + options.ticket_cmd = 'c'; + } else if (pcmk__str_any_of(option_name, "--cleanup", "-C", NULL)) { + options.ticket_cmd = 'C'; + } + + return TRUE; +} + +static gboolean +delete_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + attr_delete = g_list_append(attr_delete, strdup(optarg)); + modified = true; + return TRUE; +} + +static gboolean +get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.get_attr_name, optarg); + options.ticket_cmd = 'G'; + return TRUE; +} + +static gboolean +grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) { + g_hash_table_insert(attr_set, strdup("granted"), strdup("true")); + modified = true; + } else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) { + g_hash_table_insert(attr_set, strdup("granted"), strdup("false")); + modified = true; + } else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) { + g_hash_table_insert(attr_set, strdup("standby"), strdup("true")); + modified = true; + } else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) { + g_hash_table_insert(attr_set, strdup("standby"), strdup("false")); + modified = true; + } + + return TRUE; +} + +static gboolean +set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { + pcmk__str_update(&options.attr_name, optarg); + + if (!options.attr_name || !options.attr_value) { + return TRUE; + } + + g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value)); + pcmk__str_update(&options.attr_name, NULL); + pcmk__str_update(&options.attr_value, NULL); + + modified = true; + + return TRUE; +} + +static GOptionEntry query_entries[] = { + { "info", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the information of ticket(s)", + NULL }, + + { "details", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the details of ticket(s)", + NULL }, + + { "raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the IDs of ticket(s)", + NULL }, + + { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Query the XML of ticket(s)", + NULL }, + + { "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Display the rsc_ticket constraints that apply to ticket(s)", + NULL }, + + { NULL } +}; + +static GOptionEntry command_entries[] = { + { "grant", 'g', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Grant a ticket to this cluster site", + NULL }, + + { "revoke", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Revoke a ticket from this cluster site", + NULL }, + + { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Tell this cluster site this ticket is standby", + NULL }, + + { "activate", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, grant_standby_cb, + "Tell this cluster site this ticket is active", + NULL }, + + { NULL } +}; + +static GOptionEntry advanced_entries[] = { + { "get-attr", 'G', 0, G_OPTION_ARG_CALLBACK, get_attr_cb, + "Display the named attribute for a ticket", + "ATTRIBUTE" }, + + { "set-attr", 'S', 0, G_OPTION_ARG_CALLBACK, set_attr_cb, + "Set the named attribute for a ticket", + "ATTRIBUTE" }, + + { "delete-attr", 'D', 0, G_OPTION_ARG_CALLBACK, delete_attr_cb, + "Delete the named attribute for a ticket", + "ATTRIBUTE" }, + + { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb, + "Delete all state of a ticket at this cluster site", + NULL }, + + { NULL} +}; + +static GOptionEntry addl_entries[] = { + { "attr-value", 'v', 0, G_OPTION_ARG_CALLBACK, attr_value_cb, + "Attribute value to use with -S", + "VALUE" }, + + { "default", 'd', 0, G_OPTION_ARG_STRING, &options.attr_default, + "(Advanced) Default attribute value to display if none is found\n" + INDENT "(for use with -G)", + "VALUE" }, + + { "force", 'f', 0, G_OPTION_ARG_NONE, &options.force, + "(Advanced) Force the action to be performed", + NULL }, + + { "ticket", 't', 0, G_OPTION_ARG_STRING, &options.ticket_id, + "Ticket ID", + "ID" }, + + { "xml-file", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.xml_file, + NULL, + NULL }, + + { NULL } +}; + +static GOptionEntry deprecated_entries[] = { + { "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name, + "(Advanced) ID of the instance_attributes object to change", + "ID" }, + + { "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id, + "(Advanced) ID of the nvpair object to change/delete", + "ID" }, + + { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &options.quiet, + "Print only the value on stdout", + NULL }, + + { NULL } +}; + +static pe_ticket_t * +find_ticket(gchar *ticket_id, pe_working_set_t * data_set) +{ + return g_hash_table_lookup(data_set->tickets, ticket_id); +} + +static void +print_date(time_t time) +{ + int lpc = 0; + char date_str[26]; + + asctime_r(localtime(&time), date_str); + for (; lpc < 26; lpc++) { + if (date_str[lpc] == '\n') { + date_str[lpc] = 0; + } + } + fprintf(stdout, "'%s'", date_str); +} + +static void +print_ticket(pe_ticket_t * ticket, bool raw, bool details) +{ + if (raw) { + fprintf(stdout, "%s\n", ticket->id); + return; + } + + fprintf(stdout, "%s\t%s %s", + ticket->id, ticket->granted ? "granted" : "revoked", + ticket->standby ? "[standby]" : " "); + + if (details && g_hash_table_size(ticket->state) > 0) { + GHashTableIter iter; + const char *name = NULL; + const char *value = NULL; + int lpc = 0; + + fprintf(stdout, " ("); + + g_hash_table_iter_init(&iter, ticket->state); + while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) { + if (lpc > 0) { + fprintf(stdout, ", "); + } + fprintf(stdout, "%s=", name); + if (pcmk__str_any_of(name, "last-granted", "expires", NULL)) { + long long time_ll; + + pcmk__scan_ll(value, &time_ll, 0); + print_date((time_t) time_ll); + } else { + fprintf(stdout, "%s", value); + } + lpc++; + } + + fprintf(stdout, ")\n"); + + } else { + if (ticket->last_granted > -1) { + fprintf(stdout, " last-granted="); + print_date(ticket->last_granted); + } + fprintf(stdout, "\n"); + } + + return; +} + +static void +print_ticket_list(pe_working_set_t * data_set, bool raw, bool details) +{ + GHashTableIter iter; + pe_ticket_t *ticket = NULL; + + g_hash_table_iter_init(&iter, data_set->tickets); + + while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) { + print_ticket(ticket, raw, details); + } +} + +static int +find_ticket_state(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_state_xml) +{ + int rc = pcmk_rc_ok; + xmlNode *xml_search = NULL; + + GString *xpath = NULL; + + CRM_ASSERT(ticket_state_xml != NULL); + *ticket_state_xml = NULL; + + xpath = g_string_sized_new(1024); + g_string_append(xpath, + "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS + "/" XML_CIB_TAG_TICKETS); + + if (ticket_id != NULL) { + pcmk__g_strcat(xpath, + "/" XML_CIB_TAG_TICKET_STATE + "[@" XML_ATTR_ID "=\"", ticket_id, "\"]", NULL); + } + + rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search, + cib_sync_call | cib_scope_local | cib_xpath); + rc = pcmk_legacy2rc(rc); + g_string_free(xpath, TRUE); + + if (rc != pcmk_rc_ok) { + return rc; + } + + crm_log_xml_debug(xml_search, "Match"); + if (xml_has_children(xml_search)) { + if (ticket_id) { + fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id); + } + *ticket_state_xml = xml_search; + } else { + *ticket_state_xml = xml_search; + } + return rc; +} + +static int +find_ticket_constraints(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_cons_xml) +{ + int rc = pcmk_rc_ok; + xmlNode *xml_search = NULL; + + GString *xpath = NULL; + const char *xpath_base = NULL; + + CRM_ASSERT(ticket_cons_xml != NULL); + *ticket_cons_xml = NULL; + + xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_CONSTRAINTS); + if (xpath_base == NULL) { + crm_err(XML_CIB_TAG_CONSTRAINTS " CIB element not known (bug?)"); + return -ENOMSG; + } + + xpath = g_string_sized_new(1024); + pcmk__g_strcat(xpath, xpath_base, "/" XML_CONS_TAG_RSC_TICKET, NULL); + + if (ticket_id != NULL) { + pcmk__g_strcat(xpath, + "[@" XML_TICKET_ATTR_TICKET "=\"", ticket_id, "\"]", + NULL); + } + + rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search, + cib_sync_call | cib_scope_local | cib_xpath); + rc = pcmk_legacy2rc(rc); + g_string_free(xpath, TRUE); + + if (rc != pcmk_rc_ok) { + return rc; + } + + crm_log_xml_debug(xml_search, "Match"); + *ticket_cons_xml = xml_search; + + return rc; +} + +static int +dump_ticket_xml(cib_t * the_cib, gchar *ticket_id) +{ + int rc = pcmk_rc_ok; + xmlNode *state_xml = NULL; + + rc = find_ticket_state(the_cib, ticket_id, &state_xml); + + if (state_xml == NULL) { + return rc; + } + + fprintf(stdout, "State XML:\n"); + if (state_xml) { + char *state_xml_str = NULL; + + state_xml_str = dump_xml_formatted(state_xml); + fprintf(stdout, "\n%s", pcmk__s(state_xml_str, "<null>\n")); + free_xml(state_xml); + free(state_xml_str); + } + + return rc; +} + +static int +dump_constraints(cib_t * the_cib, gchar *ticket_id) +{ + int rc = pcmk_rc_ok; + xmlNode *cons_xml = NULL; + char *cons_xml_str = NULL; + + rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml); + + if (cons_xml == NULL) { + return rc; + } + + cons_xml_str = dump_xml_formatted(cons_xml); + fprintf(stdout, "Constraints XML:\n\n%s", + pcmk__s(cons_xml_str, "<null>\n")); + free_xml(cons_xml); + free(cons_xml_str); + + return rc; +} + +static int +get_ticket_state_attr(gchar *ticket_id, const char *attr_name, const char **attr_value, + pe_working_set_t * data_set) +{ + pe_ticket_t *ticket = NULL; + + CRM_ASSERT(attr_value != NULL); + *attr_value = NULL; + + ticket = g_hash_table_lookup(data_set->tickets, ticket_id); + if (ticket == NULL) { + return ENXIO; + } + + *attr_value = g_hash_table_lookup(ticket->state, attr_name); + if (*attr_value == NULL) { + return ENXIO; + } + + return pcmk_rc_ok; +} + +static void +ticket_warning(gchar *ticket_id, const char *action) +{ + GString *warning = g_string_sized_new(1024); + const char *word = NULL; + + CRM_ASSERT(action != NULL); + + if (strcmp(action, "grant") == 0) { + pcmk__g_strcat(warning, + "This command cannot help you verify whether '", + ticket_id, + "' has been already granted elsewhere.\n", NULL); + word = "to"; + + } else { + pcmk__g_strcat(warning, + "Revoking '", ticket_id, "' can trigger the specified " + "'loss-policy'(s) relating to '", ticket_id, "'.\n\n" + "You can check that with:\n" + "crm_ticket --ticket ", ticket_id, " --constraints\n\n" + "Otherwise before revoking '", ticket_id, "', " + "you may want to make '", ticket_id, "' " + "standby with:\n" + "crm_ticket --ticket ", ticket_id, " --standby\n\n", + NULL); + word = "from"; + } + + pcmk__g_strcat(warning, + "If you really want to ", action, " '", ticket_id, "' ", + word, " this site now, and you know what you are doing,\n" + "please specify --force.", NULL); + + fprintf(stdout, "%s\n", (const char *) warning->str); + + g_string_free(warning, TRUE); +} + +static bool +allow_modification(gchar *ticket_id) +{ + const char *value = NULL; + GList *list_iter = NULL; + + if (options.force) { + return true; + } + + if (g_hash_table_lookup_extended(attr_set, "granted", NULL, (gpointer *) & value)) { + if (crm_is_true(value)) { + ticket_warning(ticket_id, "grant"); + return false; + + } else { + ticket_warning(ticket_id, "revoke"); + return false; + } + } + + for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) { + const char *key = (const char *)list_iter->data; + + if (pcmk__str_eq(key, "granted", pcmk__str_casei)) { + ticket_warning(ticket_id, "revoke"); + return false; + } + } + + return true; +} + +static int +modify_ticket_state(gchar * ticket_id, cib_t * cib, pe_working_set_t * data_set) +{ + int rc = pcmk_rc_ok; + xmlNode *xml_top = NULL; + xmlNode *ticket_state_xml = NULL; + bool found = false; + + GList *list_iter = NULL; + GHashTableIter hash_iter; + + char *key = NULL; + char *value = NULL; + + pe_ticket_t *ticket = NULL; + + rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); + if (rc == pcmk_rc_ok) { + crm_debug("Found a match state for ticket: id=%s", ticket_id); + xml_top = ticket_state_xml; + found = true; + + } else if (rc != ENXIO) { + return rc; + + } else if (g_hash_table_size(attr_set) == 0){ + return pcmk_rc_ok; + + } else { + xmlNode *xml_obj = NULL; + + xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); + xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); + ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE); + crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id); + } + + for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) { + const char *key = (const char *)list_iter->data; + xml_remove_prop(ticket_state_xml, key); + } + + ticket = find_ticket(ticket_id, data_set); + + g_hash_table_iter_init(&hash_iter, attr_set); + while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) { + crm_xml_add(ticket_state_xml, key, value); + + if (pcmk__str_eq(key, "granted", pcmk__str_casei) + && (ticket == NULL || ticket->granted == FALSE) + && crm_is_true(value)) { + + char *now = pcmk__ttoa(time(NULL)); + + crm_xml_add(ticket_state_xml, "last-granted", now); + free(now); + } + } + + if (found && (attr_delete != NULL)) { + crm_log_xml_debug(xml_top, "Replace"); + rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options); + rc = pcmk_legacy2rc(rc); + + } else { + crm_log_xml_debug(xml_top, "Update"); + rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options); + rc = pcmk_legacy2rc(rc); + } + + free_xml(xml_top); + return rc; +} + +static int +delete_ticket_state(gchar *ticket_id, cib_t * cib) +{ + xmlNode *ticket_state_xml = NULL; + + int rc = pcmk_rc_ok; + + rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); + + if (rc == ENXIO) { + return pcmk_rc_ok; + + } else if (rc != pcmk_rc_ok) { + return rc; + } + + crm_log_xml_debug(ticket_state_xml, "Delete"); + + rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options); + rc = pcmk_legacy2rc(rc); + + if (rc == pcmk_rc_ok) { + fprintf(stdout, "Cleaned up %s\n", ticket_id); + } + + free_xml(ticket_state_xml); + return rc; +} + +static GOptionContext * +build_arg_context(pcmk__common_args_t *args) { + GOptionContext *context = NULL; + + const char *description = "Examples:\n\n" + "Display the info of tickets:\n\n" + "\tcrm_ticket --info\n\n" + "Display the detailed info of tickets:\n\n" + "\tcrm_ticket --details\n\n" + "Display the XML of 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --query-xml\n\n" + "Display the rsc_ticket constraints that apply to 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --constraints\n\n" + "Grant 'ticketA' to this cluster site:\n\n" + "\tcrm_ticket --ticket ticketA --grant\n\n" + "Revoke 'ticketA' from this cluster site:\n\n" + "\tcrm_ticket --ticket ticketA --revoke\n\n" + "Make 'ticketA' standby (the cluster site will treat a granted\n" + "'ticketA' as 'standby', and the dependent resources will be\n" + "stopped or demoted gracefully without triggering loss-policies):\n\n" + "\tcrm_ticket --ticket ticketA --standby\n\n" + "Activate 'ticketA' from being standby:\n\n" + "\tcrm_ticket --ticket ticketA --activate\n\n" + "Get the value of the 'granted' attribute for 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --get-attr granted\n\n" + "Set the value of the 'standby' attribute for 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --set-attr standby --attr-value true\n\n" + "Delete the 'granted' attribute for 'ticketA':\n\n" + "\tcrm_ticket --ticket ticketA --delete-attr granted\n\n" + "Erase the operation history of 'ticketA' at this cluster site,\n" + "causing the cluster site to 'forget' the existing ticket state:\n\n" + "\tcrm_ticket --ticket ticketA --cleanup\n\n"; + + context = pcmk__build_arg_context(args, NULL, NULL, NULL); + g_option_context_set_description(context, description); + + pcmk__add_arg_group(context, "queries", "Queries:", + "Show queries", query_entries); + pcmk__add_arg_group(context, "commands", "Commands:", + "Show command options", command_entries); + pcmk__add_arg_group(context, "advanced", "Advanced Options:", + "Show advanced options", advanced_entries); + pcmk__add_arg_group(context, "additional", "Additional Options:", + "Show additional options", addl_entries); + pcmk__add_arg_group(context, "deprecated", "Deprecated Options:", + "Show deprecated options", deprecated_entries); + + return context; +} + +int +main(int argc, char **argv) +{ + pe_working_set_t *data_set = NULL; + xmlNode *cib_xml_copy = NULL; + + cib_t *cib_conn = NULL; + crm_exit_t exit_code = CRM_EX_OK; + int rc = pcmk_rc_ok; + + pcmk__common_args_t *args = NULL; + GOptionContext *context = NULL; + gchar **processed_args = NULL; + + attr_set = pcmk__strkey_table(free, free); + attr_delete = NULL; + + args = pcmk__new_common_args(SUMMARY); + context = build_arg_context(args); + processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS"); + + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + goto done; + } + + pcmk__cli_init_logging("crm_ticket", args->verbosity); + + if (args->version) { + g_strfreev(processed_args); + pcmk__free_arg_context(context); + /* FIXME: When crm_ticket is converted to use formatted output, this can go. */ + pcmk__cli_help('v'); + } + + data_set = pe_new_working_set(); + if (data_set == NULL) { + crm_perror(LOG_CRIT, "Could not allocate working set"); + exit_code = CRM_EX_OSERR; + goto done; + } + pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat); + + cib_conn = cib_new(); + if (cib_conn == NULL) { + exit_code = CRM_EX_DISCONNECT; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB manager"); + goto done; + } + + rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not connect to the CIB: %s", + pcmk_rc_str(rc)); + goto done; + } + + if (options.xml_file != NULL) { + cib_xml_copy = filename2xml(options.xml_file); + + } else { + rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call); + rc = pcmk_legacy2rc(rc); + + if (rc != pcmk_rc_ok) { + exit_code = pcmk_rc2exitc(rc); + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Could not get local CIB: %s", + pcmk_rc_str(rc)); + goto done; + } + } + + if (!cli_config_update(&cib_xml_copy, NULL, FALSE)) { + exit_code = CRM_EX_CONFIG; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not update local CIB to latest schema version"); + goto done; + } + + data_set->input = cib_xml_copy; + data_set->now = crm_time_new(NULL); + + cluster_status(data_set); + + /* For recording the tickets that are referenced in rsc_ticket constraints + * but have never been granted yet. */ + pcmk__unpack_constraints(data_set); + + if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') { + bool raw = false; + bool details = false; + + if (options.ticket_cmd == 'L') { + details = true; + } else if (options.ticket_cmd == 'w') { + raw = true; + } + + if (options.ticket_id) { + pe_ticket_t *ticket = find_ticket(options.ticket_id, data_set); + + if (ticket == NULL) { + exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "No such ticket '%s'", options.ticket_id); + goto done; + } + print_ticket(ticket, raw, details); + + } else { + print_ticket_list(data_set, raw, details); + } + + } else if (options.ticket_cmd == 'q') { + rc = dump_ticket_xml(cib_conn, options.ticket_id); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not query ticket XML: %s", pcmk_rc_str(rc)); + } + + } else if (options.ticket_cmd == 'c') { + rc = dump_constraints(cib_conn, options.ticket_id); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not show ticket constraints: %s", pcmk_rc_str(rc)); + } + + } else if (options.ticket_cmd == 'G') { + const char *value = NULL; + + if (options.ticket_id == NULL) { + exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); + goto done; + } + + rc = get_ticket_state_attr(options.ticket_id, options.get_attr_name, &value, data_set); + if (rc == pcmk_rc_ok) { + fprintf(stdout, "%s\n", value); + } else if (rc == ENXIO && options.attr_default) { + fprintf(stdout, "%s\n", options.attr_default); + rc = pcmk_rc_ok; + } + exit_code = pcmk_rc2exitc(rc); + + } else if (options.ticket_cmd == 'C') { + if (options.ticket_id == NULL) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); + goto done; + } + + if (options.force == FALSE) { + pe_ticket_t *ticket = NULL; + + ticket = find_ticket(options.ticket_id, data_set); + if (ticket == NULL) { + exit_code = CRM_EX_NOSUCH; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "No such ticket '%s'", options.ticket_id); + goto done; + } + + if (ticket->granted) { + ticket_warning(options.ticket_id, "revoke"); + exit_code = CRM_EX_INSUFFICIENT_PRIV; + goto done; + } + } + + rc = delete_ticket_state(options.ticket_id, cib_conn); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not clean up ticket: %s", pcmk_rc_str(rc)); + } + + } else if (modified) { + if (options.ticket_id == NULL) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); + goto done; + } + + if (options.attr_value + && (pcmk__str_empty(options.attr_name))) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply attribute name with -S for -v %s", options.attr_value); + goto done; + } + + if (options.attr_name + && (pcmk__str_empty(options.attr_value))) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply attribute value with -v for -S %s", options.attr_value); + goto done; + } + + if (!allow_modification(options.ticket_id)) { + exit_code = CRM_EX_INSUFFICIENT_PRIV; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Ticket modification not allowed"); + goto done; + } + + rc = modify_ticket_state(options.ticket_id, cib_conn, data_set); + exit_code = pcmk_rc2exitc(rc); + + if (rc != pcmk_rc_ok) { + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Could not modify ticket: %s", pcmk_rc_str(rc)); + } + + } else if (options.ticket_cmd == 'S') { + /* Correct usage was handled in the "if (modified)" block above, so + * this is just for reporting usage errors + */ + + if (pcmk__str_empty(options.attr_name)) { + // We only get here if ticket_cmd was left as default + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Must supply a command"); + goto done; + } + + if (options.ticket_id == NULL) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply ticket ID with -t"); + goto done; + } + + if (pcmk__str_empty(options.attr_value)) { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Must supply value with -v for -S %s", options.attr_name); + goto done; + } + + } else { + exit_code = CRM_EX_USAGE; + g_set_error(&error, PCMK__EXITC_ERROR, exit_code, + "Unknown command: %c", options.ticket_cmd); + } + + done: + if (attr_set) { + g_hash_table_destroy(attr_set); + } + attr_set = NULL; + + if (attr_delete) { + g_list_free_full(attr_delete, free); + } + attr_delete = NULL; + + pe_free_working_set(data_set); + data_set = NULL; + + cib__clean_up_connection(&cib_conn); + + g_strfreev(processed_args); + pcmk__free_arg_context(context); + g_free(options.attr_default); + g_free(options.attr_id); + free(options.attr_name); + free(options.attr_value); + free(options.get_attr_name); + g_free(options.set_name); + g_free(options.ticket_id); + g_free(options.xml_file); + + pcmk__output_and_clear_error(&error, NULL); + + crm_exit(exit_code); +} |