| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <crm_internal.h> |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <libgen.h> |
| |
| #include <sys/param.h> |
| #include <sys/types.h> |
| |
| #include <crm/crm.h> |
| #include <crm/msg_xml.h> |
| #include <crm/common/xml_internal.h> |
| #include <crm/common/ipc.h> |
| |
| #include <crm/common/attrd_internal.h> |
| |
| static pcmk__cli_option_t long_options[] = { |
| |
| { |
| "help", no_argument, NULL, '?', |
| "\tThis text", pcmk__option_default |
| }, |
| { |
| "version", no_argument, NULL, '$', |
| "\tVersion information", pcmk__option_default |
| }, |
| { |
| "verbose", no_argument, NULL, 'V', |
| "\tIncrease debug output\n", pcmk__option_default |
| }, |
| { |
| "name", required_argument, NULL, 'n', |
| "The attribute's name", pcmk__option_default |
| }, |
| { |
| "-spacer-", no_argument, NULL, '-', |
| "\nCommands:", pcmk__option_default |
| }, |
| { |
| "update", required_argument, NULL, 'U', |
| "Update attribute's value in pacemaker-attrd. If this causes the value " |
| "to change, it will also be updated in the cluster configuration.", |
| pcmk__option_default |
| }, |
| { |
| "update-both", required_argument, NULL, 'B', |
| "Update attribute's value and time to wait (dampening) in " |
| "pacemaker-attrd. If this causes the value or dampening to change, " |
| "the attribute will also be written to the cluster configuration, " |
| "so be aware that repeatedly changing the dampening reduces its " |
| "effectiveness.", |
| pcmk__option_default |
| }, |
| { |
| "update-delay", no_argument, NULL, 'Y', |
| "Update attribute's dampening in pacemaker-attrd (requires " |
| "-d/--delay). If this causes the dampening to change, the " |
| "attribute will also be written to the cluster configuration, so " |
| "be aware that repeatedly changing the dampening reduces its " |
| "effectiveness.", |
| pcmk__option_default |
| }, |
| { |
| "query", no_argument, NULL, 'Q', |
| "\tQuery the attribute's value from pacemaker-attrd", |
| pcmk__option_default |
| }, |
| { |
| "delete", no_argument, NULL, 'D', |
| "\tDelete attribute from pacemaker-attrd. If a value was previously " |
| "set, it will also be removed from the cluster configuration", |
| pcmk__option_default |
| }, |
| { |
| "refresh", no_argument, NULL, 'R', |
| "\t(Advanced) Force the pacemaker-attrd daemon to resend all current " |
| "values to the CIB", |
| pcmk__option_default |
| }, |
| |
| { |
| "-spacer-", no_argument, NULL, '-', |
| "\nAdditional options:", pcmk__option_default |
| }, |
| { |
| "delay", required_argument, NULL, 'd', |
| "The time to wait (dampening) in seconds for further changes " |
| "before writing", |
| pcmk__option_default |
| }, |
| { |
| "set", required_argument, NULL, 's', |
| "(Advanced) The attribute set in which to place the value", |
| pcmk__option_default |
| }, |
| { |
| "node", required_argument, NULL, 'N', |
| "Set the attribute for the named node (instead of the local one)", |
| pcmk__option_default |
| }, |
| { |
| "all", no_argument, NULL, 'A', |
| "Show values of the attribute for all nodes (query only)", |
| pcmk__option_default |
| }, |
| |
| |
| { |
| "lifetime", required_argument, NULL, 'l', |
| "(Not yet implemented) Lifetime of the node attribute (silently " |
| "ignored by cluster)", |
| pcmk__option_default |
| }, |
| { |
| "private", no_argument, NULL, 'p', |
| "\tIf this creates a new attribute, never write the attribute to CIB", |
| pcmk__option_default |
| }, |
| |
| |
| { |
| "quiet", no_argument, NULL, 'q', |
| NULL, pcmk__option_hidden |
| }, |
| { |
| "update", required_argument, NULL, 'v', |
| NULL, pcmk__option_hidden |
| }, |
| { |
| "section", required_argument, NULL, 'S', |
| NULL, pcmk__option_hidden |
| }, |
| { 0, 0, 0, 0 } |
| }; |
| |
| static int do_query(const char *attr_name, const char *attr_node, gboolean query_all); |
| static int do_update(char command, const char *attr_node, const char *attr_name, |
| const char *attr_value, const char *attr_section, |
| const char *attr_set, const char *attr_dampen, int attr_options); |
| |
| |
| #define cleanup_memory() \ |
| free(attr_dampen); \ |
| free(attr_name); \ |
| free(attr_node); \ |
| free(attr_section); \ |
| free(attr_set); |
| |
| #define set_option(option_var) \ |
| if (option_var) { \ |
| free(option_var); \ |
| } \ |
| option_var = strdup(optarg); |
| |
| int |
| main(int argc, char **argv) |
| { |
| int index = 0; |
| int argerr = 0; |
| int attr_options = pcmk__node_attr_none; |
| int flag; |
| crm_exit_t exit_code = CRM_EX_OK; |
| char *attr_node = NULL; |
| char *attr_name = NULL; |
| char *attr_set = NULL; |
| char *attr_section = NULL; |
| char *attr_dampen = NULL; |
| const char *attr_value = NULL; |
| char command = 'Q'; |
| |
| gboolean query_all = FALSE; |
| |
| crm_log_cli_init("attrd_updater"); |
| pcmk__set_cli_options(NULL, "-n <attribute> <command> [options]", |
| long_options, |
| "query and update Pacemaker node attributes"); |
| |
| if (argc < 2) { |
| pcmk__cli_help('?', CRM_EX_USAGE); |
| } |
| |
| while (1) { |
| flag = pcmk__next_cli_option(argc, argv, &index, NULL); |
| if (flag == -1) |
| break; |
| |
| switch (flag) { |
| case 'V': |
| crm_bump_log_level(argc, argv); |
| break; |
| case '?': |
| case '$': |
| cleanup_memory(); |
| pcmk__cli_help(flag, CRM_EX_OK); |
| break; |
| case 'n': |
| set_option(attr_name); |
| break; |
| case 's': |
| set_option(attr_set); |
| break; |
| case 'd': |
| set_option(attr_dampen); |
| break; |
| case 'l': |
| case 'S': |
| set_option(attr_section); |
| break; |
| case 'N': |
| set_option(attr_node); |
| break; |
| case 'A': |
| query_all = TRUE; |
| break; |
| case 'p': |
| pcmk__set_node_attr_flags(attr_options, pcmk__node_attr_private); |
| break; |
| case 'q': |
| break; |
| case 'Y': |
| command = flag; |
| crm_log_args(argc, argv); |
| break; |
| case 'Q': |
| case 'B': |
| case 'R': |
| case 'D': |
| case 'U': |
| case 'v': |
| command = flag; |
| attr_value = optarg; |
| crm_log_args(argc, argv); |
| break; |
| default: |
| ++argerr; |
| break; |
| } |
| } |
| |
| if (optind > argc) { |
| ++argerr; |
| } |
| |
| if (command != 'R' && attr_name == NULL) { |
| ++argerr; |
| } |
| |
| if (argerr) { |
| cleanup_memory(); |
| pcmk__cli_help('?', CRM_EX_USAGE); |
| } |
| |
| if (command == 'Q') { |
| exit_code = crm_errno2exit(do_query(attr_name, attr_node, query_all)); |
| } else { |
| |
| |
| |
| |
| |
| exit_code = pcmk_rc2exitc(do_update(command, |
| pcmk__node_attr_target(attr_node), |
| attr_name, attr_value, |
| attr_section, attr_set, |
| attr_dampen, attr_options)); |
| } |
| |
| cleanup_memory(); |
| crm_exit(exit_code); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int |
| send_attrd_query(const char *name, const char *host, xmlNode **reply) |
| { |
| int rc; |
| crm_ipc_t *ipc; |
| xmlNode *query; |
| |
| |
| query = create_xml_node(NULL, __func__); |
| if (query == NULL) { |
| return -ENOMEM; |
| } |
| crm_xml_add(query, F_TYPE, T_ATTRD); |
| crm_xml_add(query, F_ORIG, crm_system_name); |
| crm_xml_add(query, PCMK__XA_ATTR_NODE_NAME, host); |
| crm_xml_add(query, PCMK__XA_TASK, PCMK__ATTRD_CMD_QUERY); |
| crm_xml_add(query, PCMK__XA_ATTR_NAME, name); |
| |
| |
| crm_debug("Sending query for value of %s on %s", name, (host? host : "all nodes")); |
| ipc = crm_ipc_new(T_ATTRD, 0); |
| if (crm_ipc_connect(ipc) == FALSE) { |
| crm_perror(LOG_ERR, "Connection to cluster attribute manager failed"); |
| rc = -ENOTCONN; |
| } else { |
| rc = crm_ipc_send(ipc, query, crm_ipc_client_response, 0, reply); |
| if (rc > 0) { |
| rc = pcmk_ok; |
| } |
| crm_ipc_close(ipc); |
| } |
| crm_ipc_destroy(ipc); |
| |
| free_xml(query); |
| return(rc); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int |
| validate_attrd_reply(xmlNode *reply, const char *attr_name) |
| { |
| const char *reply_attr; |
| |
| if (reply == NULL) { |
| fprintf(stderr, "Could not query value of %s: reply did not contain valid XML\n", |
| attr_name); |
| return -pcmk_err_schema_validation; |
| } |
| crm_log_xml_trace(reply, "Reply"); |
| |
| reply_attr = crm_element_value(reply, PCMK__XA_ATTR_NAME); |
| if (reply_attr == NULL) { |
| fprintf(stderr, "Could not query value of %s: attribute does not exist\n", |
| attr_name); |
| return -ENXIO; |
| } |
| |
| if (!pcmk__str_eq(crm_element_value(reply, F_TYPE), T_ATTRD, pcmk__str_casei) |
| || (crm_element_value(reply, PCMK__XA_ATTR_VERSION) == NULL) |
| || strcmp(reply_attr, attr_name)) { |
| fprintf(stderr, |
| "Could not query value of %s: reply did not contain expected identification\n", |
| attr_name); |
| return -pcmk_err_schema_validation; |
| } |
| return pcmk_ok; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static gboolean |
| print_attrd_values(xmlNode *reply, const char *attr_name) |
| { |
| xmlNode *child; |
| const char *reply_host, *reply_value; |
| gboolean have_values = FALSE; |
| |
| |
| for (child = pcmk__xml_first_child(reply); child != NULL; |
| child = pcmk__xml_next(child)) { |
| |
| if (!pcmk__str_eq((const char *)child->name, XML_CIB_TAG_NODE, |
| pcmk__str_casei)) { |
| crm_warn("Ignoring unexpected %s tag in query reply", child->name); |
| } else { |
| reply_host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME); |
| reply_value = crm_element_value(child, PCMK__XA_ATTR_VALUE); |
| |
| if (reply_host == NULL) { |
| crm_warn("Ignoring %s tag without %s attribute in query reply", |
| XML_CIB_TAG_NODE, PCMK__XA_ATTR_NODE_NAME); |
| } else { |
| printf("name=\"%s\" host=\"%s\" value=\"%s\"\n", |
| attr_name, reply_host, (reply_value? reply_value : "")); |
| have_values = TRUE; |
| } |
| } |
| } |
| return have_values; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int |
| do_query(const char *attr_name, const char *attr_node, gboolean query_all) |
| { |
| xmlNode *reply = NULL; |
| int rc; |
| |
| |
| if (query_all == TRUE) { |
| attr_node = NULL; |
| } else { |
| attr_node = pcmk__node_attr_target(attr_node); |
| } |
| |
| |
| rc = send_attrd_query(attr_name, attr_node, &reply); |
| if (rc != pcmk_ok) { |
| fprintf(stderr, "Could not query value of %s: %s (%d)\n", attr_name, pcmk_strerror(rc), rc); |
| return rc; |
| } |
| |
| |
| rc = validate_attrd_reply(reply, attr_name); |
| if (rc != pcmk_ok) { |
| if (reply != NULL) { |
| free_xml(reply); |
| } |
| return rc; |
| } |
| |
| |
| if (print_attrd_values(reply, attr_name) == FALSE) { |
| fprintf(stderr, |
| "Could not query value of %s: reply had attribute name but no host values\n", |
| attr_name); |
| free_xml(reply); |
| return -pcmk_err_schema_validation; |
| } |
| |
| return pcmk_ok; |
| } |
| |
| static int |
| do_update(char command, const char *attr_node, const char *attr_name, |
| const char *attr_value, const char *attr_section, |
| const char *attr_set, const char *attr_dampen, int attr_options) |
| { |
| int rc = pcmk__node_attr_request(NULL, command, attr_node, attr_name, |
| attr_value, attr_section, attr_set, |
| attr_dampen, NULL, attr_options); |
| if (rc != pcmk_rc_ok) { |
| fprintf(stderr, "Could not update %s=%s: %s (%d)\n", |
| attr_name, attr_value, pcmk_rc_str(rc), rc); |
| } |
| return rc; |
| } |