mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
588 lines
15 KiB
588 lines
15 KiB
|
|
/*
|
|
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <lha_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 <hb_api.h>
|
|
#include <clplumbing/uids.h>
|
|
#include <clplumbing/Gmain_timeout.h>
|
|
|
|
#include <crm/msg_xml.h>
|
|
#include <crm/common/xml.h>
|
|
#include <crm/common/ctrl.h>
|
|
#include <crm/common/ipc.h>
|
|
|
|
#include <crm/cib.h>
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
# include <getopt.h>
|
|
#endif
|
|
#include <ha_msg.h> /* someone complaining about _ha_msg_mod not being found */
|
|
|
|
int exit_code = cib_ok;
|
|
int message_timer_id = -1;
|
|
int message_timeout_ms = 30*1000;
|
|
|
|
GMainLoop *mainloop = NULL;
|
|
const char *crm_system_name = "cibadmin";
|
|
IPC_Channel *crmd_channel = NULL;
|
|
|
|
const char *host = NULL;
|
|
void usage(const char *cmd, int exit_status);
|
|
enum cib_errors do_init(void);
|
|
int do_work(crm_data_t *input, int command_options, crm_data_t **output);
|
|
|
|
gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data);
|
|
gboolean admin_message_timeout(gpointer data);
|
|
void cib_connection_destroy(gpointer user_data);
|
|
void cibadmin_op_callback(const HA_Message *msg, int call_id, int rc,
|
|
crm_data_t *output, void *user_data);
|
|
|
|
int command_options = 0;
|
|
const char *cib_action = NULL;
|
|
|
|
typedef struct str_list_s
|
|
{
|
|
int num_items;
|
|
char *value;
|
|
struct str_list_s *next;
|
|
} str_list_t;
|
|
|
|
char *this_msg_reference = NULL;
|
|
char *obj_type = NULL;
|
|
char *status = NULL;
|
|
char *migrate_from = NULL;
|
|
char *migrate_res = NULL;
|
|
char *subtype = NULL;
|
|
char *reset = NULL;
|
|
|
|
int request_id = 0;
|
|
int operation_status = 0;
|
|
cib_t *the_cib = NULL;
|
|
|
|
#define OPTARGS "V?o:QDUCEX:t:Srwlsh:MmBfbdRx:pP5"
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int argerr = 0;
|
|
int flag;
|
|
char *admin_input_xml = NULL;
|
|
char *admin_input_file = NULL;
|
|
gboolean admin_input_stdin = FALSE;
|
|
crm_data_t *output = NULL;
|
|
crm_data_t *input = NULL;
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
/* Top-level Options */
|
|
{CIB_OP_ERASE, 0, 0, 'E'},
|
|
{CIB_OP_QUERY, 0, 0, 'Q'},
|
|
{CIB_OP_CREATE, 0, 0, 'C'},
|
|
{CIB_OP_REPLACE, 0, 0, 'R'},
|
|
{CIB_OP_UPDATE, 0, 0, 'U'},
|
|
{CIB_OP_MODIFY, 0, 0, 'M'},
|
|
{"patch", 0, 0, 'P'},
|
|
{CIB_OP_DELETE, 0, 0, 'D'},
|
|
{CIB_OP_DELETE_ALT, 0, 0, 'd'},
|
|
{CIB_OP_BUMP, 0, 0, 'B'},
|
|
{CIB_OP_SYNC, 0, 0, 'S'},
|
|
{CIB_OP_SLAVE, 0, 0, 'r'},
|
|
{CIB_OP_MASTER, 0, 0, 'w'},
|
|
{CIB_OP_ISMASTER,0, 0, 'm'},
|
|
{"md5-sum", 0, 0, '5'},
|
|
|
|
{"force-quorum",0, 0, 'f'},
|
|
{"local", 0, 0, 'l'},
|
|
{"sync-call", 0, 0, 's'},
|
|
{"no-bcast", 0, 0, 'b'},
|
|
{"host", 0, 0, 'h'},
|
|
{F_CRM_DATA, 1, 0, 'X'},
|
|
{"xml-file", 1, 0, 'x'},
|
|
{"xml-pipe", 0, 0, 'p'},
|
|
{"verbose", 0, 0, 'V'},
|
|
{"help", 0, 0, '?'},
|
|
{"reference", 1, 0, 0},
|
|
{"timeout", 1, 0, 't'},
|
|
|
|
/* common options */
|
|
{"obj_type", 1, 0, 'o'},
|
|
|
|
{0, 0, 0, 0}
|
|
};
|
|
#endif
|
|
|
|
crm_log_init("cibadmin", LOG_CRIT, FALSE, FALSE, argc, argv);
|
|
|
|
if(argc < 2) {
|
|
usage(crm_system_name, LSB_EXIT_EINVAL);
|
|
}
|
|
|
|
while (1) {
|
|
#ifdef HAVE_GETOPT_H
|
|
flag = getopt_long(argc, argv, OPTARGS,
|
|
long_options, &option_index);
|
|
#else
|
|
flag = getopt(argc, argv, OPTARGS);
|
|
#endif
|
|
if (flag == -1)
|
|
break;
|
|
|
|
switch(flag) {
|
|
#ifdef HAVE_GETOPT_H
|
|
case 0:
|
|
printf("option %s",
|
|
long_options[option_index].name);
|
|
if (optarg)
|
|
printf(" with arg %s", optarg);
|
|
printf("\n");
|
|
if (safe_str_eq("reference", long_options[option_index].name)) {
|
|
this_msg_reference = crm_strdup(optarg);
|
|
|
|
} else {
|
|
printf("Long option (--%s) is not (yet?) properly supported\n",
|
|
long_options[option_index].name);
|
|
++argerr;
|
|
}
|
|
break;
|
|
#endif
|
|
case 't':
|
|
message_timeout_ms = atoi(optarg);
|
|
if(message_timeout_ms < 1) {
|
|
message_timeout_ms = 30*1000;
|
|
}
|
|
break;
|
|
|
|
case 'E':
|
|
cib_action = CIB_OP_ERASE;
|
|
break;
|
|
case 'Q':
|
|
cib_action = CIB_OP_QUERY;
|
|
break;
|
|
case 'P':
|
|
cib_action = CIB_OP_APPLY_DIFF;
|
|
break;
|
|
case 'S':
|
|
cib_action = CIB_OP_SYNC;
|
|
break;
|
|
case 'U':
|
|
case 'M':
|
|
cib_action = CIB_OP_MODIFY;
|
|
break;
|
|
case 'R':
|
|
cib_action = CIB_OP_REPLACE;
|
|
break;
|
|
case 'C':
|
|
cib_action = CIB_OP_CREATE;
|
|
break;
|
|
case 'D':
|
|
cib_action = CIB_OP_DELETE;
|
|
break;
|
|
case '5':
|
|
cib_action = "md5-sum";
|
|
break;
|
|
case 'd':
|
|
cib_action = CIB_OP_DELETE_ALT;
|
|
break;
|
|
case 'm':
|
|
cib_action = CIB_OP_ISMASTER;
|
|
command_options |= cib_scope_local;
|
|
break;
|
|
case 'B':
|
|
cib_action = CIB_OP_BUMP;
|
|
break;
|
|
case 'r':
|
|
cib_action = CIB_OP_SLAVE;
|
|
break;
|
|
case 'w':
|
|
cib_action = CIB_OP_MASTER;
|
|
command_options |= cib_scope_local;
|
|
break;
|
|
case 'V':
|
|
command_options = command_options | cib_verbose;
|
|
cl_log_enable_stderr(TRUE);
|
|
alter_debug(DEBUG_INC);
|
|
break;
|
|
case '?':
|
|
usage(crm_system_name, LSB_EXIT_OK);
|
|
break;
|
|
case 'o':
|
|
crm_debug_2("Option %c => %s", flag, optarg);
|
|
obj_type = crm_strdup(optarg);
|
|
break;
|
|
case 'X':
|
|
crm_debug_2("Option %c => %s", flag, optarg);
|
|
admin_input_xml = crm_strdup(optarg);
|
|
break;
|
|
case 'x':
|
|
crm_debug_2("Option %c => %s", flag, optarg);
|
|
admin_input_file = crm_strdup(optarg);
|
|
break;
|
|
case 'p':
|
|
admin_input_stdin = TRUE;
|
|
break;
|
|
case 'h':
|
|
host = crm_strdup(optarg);
|
|
break;
|
|
case 'l':
|
|
command_options |= cib_scope_local;
|
|
break;
|
|
case 'b':
|
|
command_options |= cib_inhibit_bcast;
|
|
command_options |= cib_scope_local;
|
|
break;
|
|
case 's':
|
|
command_options |= cib_sync_call;
|
|
break;
|
|
case 'f':
|
|
command_options |= cib_quorum_override;
|
|
break;
|
|
default:
|
|
printf("Argument code 0%o (%c)"
|
|
" is not (?yet?) supported\n",
|
|
flag, flag);
|
|
++argerr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind < argc) {
|
|
printf("non-option ARGV-elements: ");
|
|
while (optind < argc)
|
|
printf("%s ", argv[optind++]);
|
|
printf("\n");
|
|
}
|
|
|
|
if (optind > argc) {
|
|
++argerr;
|
|
}
|
|
|
|
if(cib_action == NULL) {
|
|
usage(crm_system_name, cib_operation);
|
|
}
|
|
|
|
if (argerr) {
|
|
usage(crm_system_name, LSB_EXIT_GENERIC);
|
|
}
|
|
|
|
if(admin_input_file != NULL) {
|
|
FILE *xml_strm = fopen(admin_input_file, "r");
|
|
input = file2xml(xml_strm, FALSE);
|
|
if(input == NULL) {
|
|
fprintf(stderr, "Couldn't parse input file: %s\n", admin_input_file);
|
|
return 1;
|
|
}
|
|
fclose(xml_strm);
|
|
|
|
} else if(admin_input_xml != NULL) {
|
|
input = string2xml(admin_input_xml);
|
|
if(input == NULL) {
|
|
fprintf(stderr, "Couldn't parse input string: %s\n", admin_input_xml);
|
|
return 1;
|
|
}
|
|
|
|
} else if(admin_input_stdin) {
|
|
input = stdin2xml();
|
|
if(input == NULL) {
|
|
fprintf(stderr, "Couldn't parse input from STDIN.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if(input != NULL) {
|
|
crm_log_xml_debug(input, "[admin input]");
|
|
}
|
|
|
|
if(safe_str_eq(cib_action, "md5-sum")) {
|
|
char *digest = NULL;
|
|
if(input == NULL) {
|
|
fprintf(stderr,
|
|
"Please supply XML to process with -X, -x or -p\n");
|
|
exit(1);
|
|
}
|
|
|
|
digest = calculate_xml_digest(input, FALSE, FALSE);
|
|
fprintf(stderr, "Digest: ");
|
|
fprintf(stdout, "%s\n", crm_str(digest));
|
|
crm_free(digest);
|
|
exit(0);
|
|
}
|
|
|
|
exit_code = do_init();
|
|
if(exit_code != cib_ok) {
|
|
crm_err("Init failed, could not perform requested operations");
|
|
fprintf(stderr, "Init failed, could not perform requested operations\n");
|
|
return -exit_code;
|
|
}
|
|
|
|
exit_code = do_work(input, command_options, &output);
|
|
if (exit_code > 0) {
|
|
/* wait for the reply by creating a mainloop and running it until
|
|
* the callbacks are invoked...
|
|
*/
|
|
request_id = exit_code;
|
|
|
|
add_cib_op_callback(
|
|
request_id, FALSE, NULL, cibadmin_op_callback);
|
|
|
|
mainloop = g_main_new(FALSE);
|
|
|
|
crm_debug("Setting operation timeout to %dms for call %d",
|
|
message_timeout_ms, request_id);
|
|
|
|
message_timer_id = Gmain_timeout_add(
|
|
message_timeout_ms, admin_message_timeout, NULL);
|
|
|
|
crm_debug_3("%s waiting for reply from the local CIB",
|
|
crm_system_name);
|
|
|
|
crm_info("Starting mainloop");
|
|
g_main_run(mainloop);
|
|
|
|
} else if(exit_code < 0) {
|
|
crm_err("Call failed: %s", cib_error2string(exit_code));
|
|
fprintf(stderr, "Call failed: %s\n",
|
|
cib_error2string(exit_code));
|
|
operation_status = exit_code;
|
|
}
|
|
|
|
|
|
if(output != NULL) {
|
|
char *buffer = dump_xml_formatted(output);
|
|
fprintf(stdout, "%s", crm_str(buffer));
|
|
crm_free(buffer);
|
|
}
|
|
|
|
crm_debug_3("%s exiting normally", crm_system_name);
|
|
return -exit_code;
|
|
}
|
|
|
|
int
|
|
do_work(crm_data_t *input, int call_options, crm_data_t **output)
|
|
{
|
|
/* construct the request */
|
|
if (strcasecmp(CIB_OP_SYNC, cib_action) == 0) {
|
|
crm_debug_4("Performing %s op...", cib_action);
|
|
return the_cib->cmds->sync_from(
|
|
the_cib, host, obj_type, call_options);
|
|
|
|
} else if (strcasecmp(CIB_OP_SLAVE, cib_action) == 0
|
|
&& (call_options ^ cib_scope_local) ) {
|
|
crm_debug_4("Performing %s op on all nodes...", cib_action);
|
|
return the_cib->cmds->set_slave_all(the_cib, call_options);
|
|
|
|
} else if (strcasecmp(CIB_OP_MASTER, cib_action) == 0) {
|
|
crm_debug_4("Performing %s op on all nodes...", cib_action);
|
|
return the_cib->cmds->set_master(the_cib, call_options);
|
|
|
|
|
|
} else if(cib_action != NULL) {
|
|
crm_debug_4("Passing \"%s\" to variant_op...", cib_action);
|
|
if(strcasecmp(CIB_OP_APPLY_DIFF, cib_action) != 0
|
|
&& input != NULL
|
|
&& do_id_check(input, NULL, TRUE, FALSE)) {
|
|
crm_err("ID Check failed.");
|
|
return cib_id_check;
|
|
}
|
|
|
|
return the_cib->cmds->variant_op(
|
|
the_cib, cib_action, host, obj_type,
|
|
input, output, call_options);
|
|
|
|
} else {
|
|
crm_err("You must specify an operation");
|
|
}
|
|
return cib_operation;
|
|
}
|
|
|
|
enum cib_errors
|
|
do_init(void)
|
|
{
|
|
enum cib_errors rc = cib_ok;
|
|
|
|
the_cib = cib_new();
|
|
rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
|
|
if(rc != cib_ok) {
|
|
crm_err("Signon to CIB failed: %s",
|
|
cib_error2string(rc));
|
|
fprintf(stderr, "Signon to CIB failed: %s\n",
|
|
cib_error2string(rc));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
void
|
|
usage(const char *cmd, int exit_status)
|
|
{
|
|
FILE *stream;
|
|
|
|
stream = exit_status != 0 ? stderr : stdout;
|
|
|
|
fprintf(stream, "usage: %s [%s] command\n"
|
|
"\twhere necessary, XML data will be obtained using -X,"
|
|
" -x, or -p options\n", cmd, OPTARGS);
|
|
|
|
fprintf(stream, "Options\n");
|
|
fprintf(stream, "\t--%s (-%c) <type>\tobject type being operated on\n",
|
|
"obj_type", 'o');
|
|
fprintf(stream, "\t\tValid values are: nodes, resources, constraints, crm_config, status\n");
|
|
fprintf(stream, "\t--%s (-%c)\tturn on debug info."
|
|
" additional instance increase verbosity\n", "verbose", 'V');
|
|
fprintf(stream, "\t--%s (-%c)\tthis help message\n", "help", '?');
|
|
fprintf(stream, "\nCommands\n");
|
|
fprintf(stream, "\t--%s (-%c)\tErase the contents of the whole CIB\n", CIB_OP_ERASE, 'E');
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_QUERY, 'Q');
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_CREATE, 'C');
|
|
fprintf(stream, "\t--%s (-%c)\tCalculate an XML file's digest."
|
|
" Requires either -X, -x or -p\n", "md5-sum", '5');
|
|
fprintf(stream, "\t--%s (-%c)\tRecursivly replace an object in the CIB\n", CIB_OP_REPLACE,'R');
|
|
fprintf(stream, "\t--%s (-%c)\tRecursivly update an object in the CIB\n", CIB_OP_UPDATE, 'U');
|
|
fprintf(stream, "\t--%s (-%c)\tFind the object somewhere in the CIB's XML tree and update is as --"CIB_OP_UPDATE" would\n", CIB_OP_MODIFY, 'M');
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE, 'D');
|
|
fprintf(stream, "\t\t\tDelete the first object matching the supplied criteria\n");
|
|
fprintf(stream, "\t\t\tEg. <op id=\"rsc1_op1\" name=\"monitor\"/>\n");
|
|
fprintf(stream, "\t\t\tThe tagname and all attributes must match in order for the element to be deleted\n");
|
|
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_DELETE_ALT, 'd');
|
|
fprintf(stream, "\t\t\tDelete the object at specified fully qualified location\n");
|
|
fprintf(stream, "\t\t\tEg. <resource id=\"rsc1\"><operations><op id=\"rsc1_op1\"/>...\n");
|
|
fprintf(stream, "\t\t\tRequires -o\n");
|
|
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_BUMP, 'B');
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_ISMASTER,'m');
|
|
fprintf(stream, "\t--%s (-%c)\t\n", CIB_OP_SYNC, 'S');
|
|
fprintf(stream, "\nXML data\n");
|
|
fprintf(stream, "\t--%s (-%c) <string>\t\tRetrieve XML from the supplied string\n", F_CRM_DATA, 'X');
|
|
fprintf(stream, "\t--%s (-%c) <filename>\tRetrieve XML from the named file\n", "xml-file", 'x');
|
|
fprintf(stream, "\t--%s (-%c)\t\t\tRetrieve XML from STDIN\n", "xml-pipe", 'p');
|
|
fprintf(stream, "\nAdvanced Options\n");
|
|
fprintf(stream, "\t--%s (-%c)\tsend command to specified host."
|
|
" Applies to %s and %s commands only\n", "host", 'h',
|
|
CIB_OP_QUERY, CIB_OP_SYNC);
|
|
fprintf(stream, "\t--%s (-%c)\tcommand takes effect locally"
|
|
" on the specified host\n", "local", 'l');
|
|
fprintf(stream, "\t--%s (-%c)\tcommand will not be broadcast even if"
|
|
" it altered the CIB\n", "no-bcast", 'b');
|
|
fprintf(stream, "\t--%s (-%c)\twait for call to complete before"
|
|
" returning\n", "sync-call", 's');
|
|
|
|
fflush(stream);
|
|
|
|
exit(exit_status);
|
|
}
|
|
|
|
|
|
gboolean
|
|
admin_message_timeout(gpointer data)
|
|
{
|
|
if(safe_str_eq(cib_action, CIB_OP_SLAVE)) {
|
|
exit_code = cib_ok;
|
|
fprintf(stdout, "CIB service(s) are in slave mode.\n");
|
|
|
|
} else {
|
|
exit_code = cib_reply_failed;
|
|
fprintf(stderr,
|
|
"No messages received in %d seconds.. aborting\n",
|
|
(int)message_timeout_ms/1000);
|
|
crm_err("No messages received in %d seconds",
|
|
(int)message_timeout_ms/1000);
|
|
}
|
|
|
|
g_main_quit(mainloop);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
cib_connection_destroy(gpointer user_data)
|
|
{
|
|
crm_err("Connection to the CIB terminated... exiting");
|
|
g_main_quit(mainloop);
|
|
return;
|
|
}
|
|
|
|
void
|
|
cibadmin_op_callback(const HA_Message *msg, int call_id, int rc,
|
|
crm_data_t *output, void *user_data)
|
|
{
|
|
char *admin_input_xml = NULL;
|
|
|
|
crm_info("our callback was invoked");
|
|
crm_log_message(LOG_MSG, msg);
|
|
exit_code = rc;
|
|
|
|
if(output != NULL) {
|
|
admin_input_xml = dump_xml_formatted(output);
|
|
}
|
|
|
|
if(safe_str_eq(cib_action, CIB_OP_ISMASTER) && rc != cib_ok) {
|
|
crm_info("CIB on %s is _not_ the master instance",
|
|
host?host:"localhost");
|
|
fprintf(stderr, "CIB on %s is _not_ the master instance\n",
|
|
host?host:"localhost");
|
|
|
|
} else if(safe_str_eq(cib_action, CIB_OP_ISMASTER)) {
|
|
crm_info("CIB on %s _is_ the master instance",
|
|
host?host:"localhost");
|
|
fprintf(stderr, "CIB on %s _is_ the master instance\n",
|
|
host?host:"localhost");
|
|
|
|
} else if(rc != 0) {
|
|
crm_warn("Call %s failed (%d): %s",
|
|
cib_action, rc, cib_error2string(rc));
|
|
fprintf(stderr, "Call %s failed (%d): %s\n",
|
|
cib_action, rc, cib_error2string(rc));
|
|
fprintf(stdout, "%s\n", crm_str(admin_input_xml));
|
|
|
|
} else if(safe_str_eq(cib_action, CIB_OP_QUERY) && output==NULL) {
|
|
crm_err("Output expected in query response");
|
|
crm_log_message(LOG_ERR, msg);
|
|
|
|
} else if(output == NULL) {
|
|
crm_info("Call passed");
|
|
|
|
} else {
|
|
crm_info("Call passed");
|
|
fprintf(stdout, "%s\n", crm_str(admin_input_xml));
|
|
}
|
|
crm_free(admin_input_xml);
|
|
|
|
if(call_id == request_id) {
|
|
g_main_quit(mainloop);
|
|
|
|
} else {
|
|
crm_info("Message was not the response we were looking for (%d vs. %d", call_id, request_id);
|
|
}
|
|
}
|