diff --git a/crm/admin/Makefile.am b/crm/admin/Makefile.am index d68e8c9490..db60e7953d 100644 --- a/crm/admin/Makefile.am +++ b/crm/admin/Makefile.am @@ -1,80 +1,85 @@ # # Copyright (C) 2004 Andrew Beekhof # # 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 # of the License, or (at your option) any later version. # # This program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir) -I$(top_srcdir) hadir = $(sysconfdir)/ha.d halibdir = $(libdir)/@HB_PKG@ commmoddir = $(halibdir)/modules/comm havarlibdir = $(localstatedir)/lib/@HB_PKG@ PIDFILE = $(localstatedir)/run/crmd.pid XML_FLAGS = `xml2-config --cflags` XML_LIBS = `xml2-config --libs` # sockets with path crmdir = $(havarlibdir)/crm apigid = @HA_APIGID@ crmuid = @HA_CCMUID@ COMMONLIBS = $(CRM_DEBUG_LIBS) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/$(CRM_DIR)/common/libcrmcommon.la \ $(top_builddir)/lib/$(CRM_DIR)/cib/libcib.la \ $(top_builddir)/lib/apphb/libapphb.la \ $(top_builddir)/lib/hbclient/libhbclient.la \ $(GLIBLIB) \ $(LIBRT) LIBRT = @LIBRT@ AM_CFLAGS = @CFLAGS@ \ -DPIDFILE='"$(PIDFILE)"' \ $(CRM_DEBUG_FLAGS) ## libraries lib_LTLIBRARIES = ## binary progs -halib_PROGRAMS = crmadmin cibadmin +halib_PROGRAMS = crmadmin cibadmin ccm_epoche ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = crmadmin_SOURCES = crmadmin.c crmadmin_CFLAGS = $(XML_FLAGS) -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' crmadmin_LDFLAGS = $(XML_LIBS) crmadmin_LDADD = $(COMMONLIBS) cibadmin_SOURCES = cibadmin.c cibadmin_CFLAGS = $(XML_FLAGS) -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' cibadmin_LDFLAGS = $(XML_LIBS) cibadmin_LDADD = $(COMMONLIBS) +ccm_epoche_SOURCES = ccm_epoche.c +ccm_epoche_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' +ccm_epoche_LDADD = $(COMMONLIBS) \ + $(top_builddir)/membership/ccm/libccmclient.la + clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/crm/admin/ccm_epoche.c b/crm/admin/ccm_epoche.c new file mode 100644 index 0000000000..5147f68a54 --- /dev/null +++ b/crm/admin/ccm_epoche.c @@ -0,0 +1,210 @@ +/* $Id: ccm_epoche.c,v 1.1 2005/02/20 14:38:54 andrew Exp $ */ +/* + * Copyright (C) 2004 Andrew Beekhof + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +const char* crm_system_name = "ccm_age"; +oc_ev_t *ccm_token = NULL; + +#define OPTARGS "hV" + +void oc_ev_special(const oc_ev_t *, oc_ev_class_t , int ); + +static void ccm_age_callback( + oc_ed_t event, void *cookie, size_t size, const void *data); +gboolean ccm_age_connect(int *ccm_fd); +void usage(const char* cmd, int exit_status); +char *lookup_host = NULL; + +int +main(int argc, char ** argv) +{ + int flag; + int argerr = 0; + int ccm_fd = 0; + fd_set rset; + oc_ev_t *ccm_token = NULL; + + crm_log_init(crm_system_name); + + while ((flag = getopt(argc, argv, OPTARGS)) != EOF) { + switch(flag) { + case 'V': + cl_log_enable_stderr(TRUE); + alter_debug(DEBUG_INC); + break; + case 'h': /* Help message */ + usage(crm_system_name, LSB_EXIT_OK); + break; + default: + ++argerr; + break; + } + } + + if (optind > argc) { + ++argerr; + } + + if (argerr) { + usage(crm_system_name,LSB_EXIT_GENERIC); + } + + if(ccm_age_connect(&ccm_fd)) { + for (;;) { + + FD_ZERO(&rset); + FD_SET(ccm_fd, &rset); + + if(select(ccm_fd + 1, &rset, NULL,NULL,NULL) == -1){ + perror("select"); + return(1); + } + if(oc_ev_handle_event(ccm_token)){ + crm_err("terminating"); + return(1); + } + } + } + return(1); +} + + +void +usage(const char* cmd, int exit_status) +{ + FILE* stream; + + stream = exit_status ? stderr : stdout; + + fprintf(stream, "usage: %s [-V]\n", cmd); + fflush(stream); + + exit(exit_status); +} + +gboolean +ccm_age_connect(int *ccm_fd) +{ + gboolean did_fail = FALSE; + int ret = 0; + + crm_debug("Registering with CCM"); + ret = oc_ev_register(&ccm_token); + if (ret != 0) { + crm_warn("CCM registration failed"); + did_fail = TRUE; + } + + if(did_fail == FALSE) { + crm_debug("Setting up CCM callbacks"); + ret = oc_ev_set_callback(ccm_token, OC_EV_MEMB_CLASS, + ccm_age_callback, NULL); + if (ret != 0) { + crm_warn("CCM callback not set"); + did_fail = TRUE; + } + } + if(did_fail == FALSE) { + oc_ev_special(ccm_token, OC_EV_MEMB_CLASS, 0/*don't care*/); + + crm_debug("Activating CCM token"); + ret = oc_ev_activate(ccm_token, ccm_fd); + if (ret != 0){ + crm_warn("CCM Activation failed"); + did_fail = TRUE; + } + } + + return !did_fail; +} + + +static void +ccm_age_callback(oc_ed_t event, void *cookie, size_t size, const void *data) +{ + int lpc; + int node_list_size; + const oc_ev_membership_t *oc = (const oc_ev_membership_t *)data; + + crm_devel("-----------------------"); + crm_devel("trans=%d, nodes=%d, new=%d, lost=%d n_idx=%d, " + "new_idx=%d, old_idx=%d", + oc->m_instance, + oc->m_n_member, oc->m_n_in, oc->m_n_out, + oc->m_memb_idx, oc->m_in_idx, oc->m_out_idx); + + if(crm_log_level >= LOG_DEV) { + node_list_size = oc->m_n_member; + for(lpc=0; lpcm_array[oc->m_memb_idx+lpc].node_uname, + oc->m_array[oc->m_memb_idx+lpc].node_id, + oc->m_array[oc->m_memb_idx+lpc].node_born_on); + } + + for(lpc=0; lpcm_n_in; lpc++) { + crm_devel("\tNEW: %s [nodeid=%d, born=%d]", + oc->m_array[oc->m_in_idx+lpc].node_uname, + oc->m_array[oc->m_in_idx+lpc].node_id, + oc->m_array[oc->m_in_idx+lpc].node_born_on); + } + + for(lpc=0; lpcm_n_out; lpc++) { + crm_devel("\tLOST: %s [nodeid=%d, born=%d]", + oc->m_array[oc->m_out_idx+lpc].node_uname, + oc->m_array[oc->m_out_idx+lpc].node_id, + oc->m_array[oc->m_out_idx+lpc].node_born_on); + } + } + crm_devel("-----------------------"); + + crm_debug("Searching %d members for our birth", oc->m_n_member); + for(lpc = 0; lpc < oc->m_n_member; lpc++) { + if(oc_ev_is_my_nodeid(ccm_token, &(oc->m_array[lpc]))){ + crm_debug("MATCH: nodeid=%d, uname=%s, born=%d", + oc->m_array[oc->m_memb_idx+lpc].node_id, + oc->m_array[oc->m_memb_idx+lpc].node_uname, + oc->m_array[oc->m_memb_idx+lpc].node_born_on); + fprintf(stdout, "%d\n", + oc->m_array[oc->m_memb_idx+lpc].node_born_on); + fflush(stdout); + exit(0); + } + } + oc_ev_callback_done(cookie); +} diff --git a/crm/admin/cibadmin.c b/crm/admin/cibadmin.c index 055e1f07ad..2655058f1f 100644 --- a/crm/admin/cibadmin.c +++ b/crm/admin/cibadmin.c @@ -1,576 +1,574 @@ -/* $Id: cibadmin.c,v 1.24 2005/02/19 18:20:25 andrew Exp $ */ +/* $Id: cibadmin.c,v 1.25 2005/02/20 14:38:54 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* someone complaining about _ha_msg_mod not being found */ #include 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(const char *xml_text, int command_options, crm_data_t **output); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); crm_data_t *handleCibMod(const char *xml); 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); 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 *id = NULL; char *this_msg_reference = NULL; char *obj_type = NULL; char *clear = 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?i:o:QDUCEX:t:Srwlsh:MB" int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; - int level = 0; char *admin_input_xml = NULL; crm_data_t *output = NULL; static struct option long_options[] = { /* Top-level Options */ {CRM_OP_CIB_ERASE, 0, 0, 'E'}, {CRM_OP_CIB_QUERY, 0, 0, 'Q'}, {CRM_OP_CIB_CREATE, 0, 0, 'C'}, {CRM_OP_CIB_REPLACE, 0, 0, 'R'}, {CRM_OP_CIB_UPDATE, 0, 0, 'U'}, {CRM_OP_CIB_DELETE, 0, 0, 'D'}, {CRM_OP_CIB_BUMP, 0, 0, 'B'}, {CRM_OP_CIB_SYNC, 0, 0, 'S'}, {CRM_OP_CIB_SLAVE, 0, 0, 'r'}, {CRM_OP_CIB_MASTER, 0, 0, 'w'}, {CRM_OP_CIB_ISMASTER,0, 0, 'M'}, {"local", 0, 0, 'l'}, {"sync-call", 0, 0, 's'}, {"host", 0, 0, 'h'}, {F_CRM_DATA, 1, 0, 'X'}, {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"reference", 1, 0, 0}, {XML_ATTR_TIMEOUT, 1, 0, 't'}, /* common options */ {XML_ATTR_ID, 1, 0, 'i'}, {"obj_type", 1, 0, 'o'}, {0, 0, 0, 0} }; crm_log_init(crm_system_name); if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } while (1) { flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); if (flag == -1) break; switch(flag) { 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; case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30*1000; } break; case 'E': cib_action = CRM_OP_CIB_ERASE; break; case 'Q': cib_action = CRM_OP_CIB_QUERY; break; case 'S': cib_action = CRM_OP_CIB_SYNC; break; case 'U': cib_action = CRM_OP_CIB_UPDATE; break; case 'R': cib_action = CRM_OP_CIB_REPLACE; break; case 'C': cib_action = CRM_OP_CIB_CREATE; break; case 'D': cib_action = CRM_OP_CIB_DELETE; break; case 'M': cib_action = CRM_OP_CIB_ISMASTER; command_options |= cib_scope_local; break; case 'B': cib_action = CRM_OP_CIB_BUMP; break; case 'r': cib_action = CRM_OP_CIB_SLAVE; break; case 'w': cib_action = CRM_OP_CIB_MASTER; command_options |= cib_scope_local; break; case 'V': - level = get_crm_log_level(); command_options = command_options | cib_verbose; cl_log_enable_stderr(TRUE); - set_crm_log_level(level+1); + alter_debug(DEBUG_INC); break; case '?': usage(crm_system_name, LSB_EXIT_OK); break; case 'i': crm_verbose("Option %c => %s", flag, optarg); id = crm_strdup(optarg); break; case 'o': crm_verbose("Option %c => %s", flag, optarg); obj_type = crm_strdup(optarg); break; case 'X': admin_input_xml = crm_strdup(optarg); break; case 'h': host = crm_strdup(optarg); break; case 'l': command_options |= cib_scope_local; break; case 's': command_options |= cib_sync_call; 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); } 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(admin_input_xml, command_options, &output); if (exit_code > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ IPC_Channel *ch = the_cib->cmds->channel(the_cib); request_id = exit_code; if(ch == NULL) { crm_err("Connection to CIB is corrupt"); return 2; } mainloop = g_main_new(FALSE); crm_devel("Setting operation timeout to %dms", message_timeout_ms); message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); crm_devel("%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_devel("%s exiting normally", crm_system_name); return -exit_code; } crm_data_t* handleCibMod(const char *xml) { const char *attr_name = NULL; const char *attr_value = NULL; crm_data_t *fragment = NULL; crm_data_t *cib_object = NULL; if(xml == NULL) { cib_object = file2xml(stdin); } else { cib_object = string2xml(xml); } if(cib_object == NULL) { return NULL; } attr_name = XML_ATTR_ID; attr_value = crm_element_value(cib_object, attr_name); if(attr_name == NULL || strlen(attr_name) == 0) { crm_err("No value for %s specified.", attr_name); return NULL; } crm_trace("Object creation complete"); /* create the cib request */ fragment = create_cib_fragment(cib_object, NULL); return fragment; } int do_work(const char *admin_input_xml, int call_options, crm_data_t **output) { /* construct the request */ crm_data_t *msg_data = NULL; char *obj_type_parent = NULL; obj_type_parent = cib_pluralSection(obj_type); if(strcmp(CRM_OP_CIB_QUERY, cib_action) == 0) { crm_verbose("Querying the CIB for section: %s", obj_type_parent); return the_cib->cmds->query_from( the_cib, host, obj_type_parent, output, call_options); } else if (strcmp(CRM_OP_CIB_ERASE, cib_action) == 0) { crm_trace("CIB Erase op in progress"); return the_cib->cmds->erase(the_cib, output, call_options); } else if (strcmp(CRM_OP_CIB_CREATE, cib_action) == 0) { enum cib_errors rc = cib_ok; crm_trace("Performing %s op...", cib_action); msg_data = handleCibMod(admin_input_xml); rc = the_cib->cmds->create( the_cib, obj_type_parent, msg_data, output, call_options); free_xml(msg_data); return rc; } else if (strcmp(CRM_OP_CIB_UPDATE, cib_action) == 0) { enum cib_errors rc = cib_ok; crm_trace("Performing %s op...", cib_action); msg_data = handleCibMod(admin_input_xml); rc = the_cib->cmds->modify( the_cib, obj_type_parent, msg_data, output, call_options); free_xml(msg_data); return rc; } else if (strcmp(CRM_OP_CIB_DELETE, cib_action) == 0) { enum cib_errors rc = cib_ok; crm_trace("Performing %s op...", cib_action); msg_data = handleCibMod(admin_input_xml); rc = the_cib->cmds->delete( the_cib, obj_type_parent, msg_data, output, call_options); free_xml(msg_data); return rc; } else if (strcmp(CRM_OP_CIB_SYNC, cib_action) == 0) { crm_trace("Performing %s op...", cib_action); return the_cib->cmds->sync_from( the_cib, host, obj_type_parent, call_options); } else if (strcmp(CRM_OP_CIB_SLAVE, cib_action) == 0 && (call_options ^ cib_scope_local) ) { crm_trace("Performing %s op on all nodes...", cib_action); return the_cib->cmds->set_slave_all(the_cib, call_options); } else if (strcmp(CRM_OP_CIB_MASTER, cib_action) == 0) { crm_trace("Performing %s op on all nodes...", cib_action); return the_cib->cmds->set_master(the_cib, call_options); } else if(cib_action != NULL) { crm_trace("Passing \"%s\" to variant_op...", cib_action); return the_cib->cmds->variant_op( the_cib, cib_action, host, obj_type_parent, NULL, 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; #ifdef USE_LIBXML /* docs say only do this once, but in their code they do it every time! */ xmlInitParser(); #endif the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, 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)); } else { rc = the_cib->cmds->set_op_callback( the_cib, cibadmin_op_callback); if(rc != cib_ok) { crm_err("Failed to set callback: %s", cib_error2string(rc)); fprintf(stderr,"Failed to set callback: %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 [-?Vio] command\n" "\twhere necessary, XML data will be expected using -X" " or on STDIN if -X isnt specified\n", cmd); fprintf(stream, "Options\n"); fprintf(stream, "\t--%s (-%c) \tid of the object being operated on\n", XML_ATTR_ID, 'i'); fprintf(stream, "\t--%s (-%c) \tobject type being operated on\n", "obj_type", 'o'); 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)\t\n", CRM_OP_CIB_ERASE, 'E'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_QUERY, 'Q'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_CREATE, 'C'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_REPLACE,'R'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_UPDATE, 'U'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_DELETE, 'D'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_BUMP, 'B'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_ISMASTER,'M'); fprintf(stream, "\t--%s (-%c)\t\n", CRM_OP_CIB_SYNC, 'S'); fprintf(stream, "\nXML data\n"); fprintf(stream, "\t--%s (-%c) \t\n", F_CRM_DATA, 'X'); 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', CRM_OP_CIB_QUERY, CRM_OP_CIB_SYNC); fprintf(stream, "\t--%s (-%c)\tcommand only takes effect locally" " on the specified host\n", "local", 'l'); 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, CRM_OP_CIB_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) { 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, CRM_OP_CIB_ISMASTER) && rc != cib_ok) { crm_info("Local CIB is _not_ the master instance"); fprintf(stderr, "Local CIB is _not_ the master instance\n"); } else if(safe_str_eq(cib_action, CRM_OP_CIB_ISMASTER)) { crm_info("Local CIB _is_ the master instance\n"); fprintf(stderr, "Local CIB _is_ the master instance\n"); } else if(rc != 0) { crm_warn("Call %s failed (%d): %s\n", 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, CRM_OP_CIB_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); } } diff --git a/crm/admin/crmadmin.c b/crm/admin/crmadmin.c index d86e8d1423..4ad997eaed 100644 --- a/crm/admin/crmadmin.c +++ b/crm/admin/crmadmin.c @@ -1,846 +1,844 @@ -/* $Id: crmadmin.c,v 1.28 2005/02/19 18:20:25 andrew Exp $ */ +/* $Id: crmadmin.c,v 1.29 2005/02/20 14:38:54 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int message_timer_id = -1; int message_timeout_ms = 30*1000; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; char *admin_uuid = NULL; void usage(const char *cmd, int exit_status); ll_cluster_t *do_init(void); int do_work(ll_cluster_t * hb_cluster); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); char *pluralSection(const char *a_section); crm_data_t *handleCibMod(void); int do_find_resource(const char *rsc, crm_data_t *xml_node); int do_find_resource_list(crm_data_t *xml_node); int do_find_node_list(crm_data_t *xml_node); gboolean admin_message_timeout(gpointer data); gboolean is_node_online(crm_data_t *node_state); enum debug { debug_none, debug_dec, debug_inc }; gboolean BE_VERBOSE = FALSE; int expected_responses = 1; gboolean DO_HEALTH = FALSE; gboolean DO_RESET = FALSE; gboolean DO_RESOURCE = FALSE; gboolean DO_ELECT_DC = FALSE; gboolean DO_WHOIS_DC = FALSE; gboolean DO_NODE_LIST = FALSE; gboolean BE_SILENT = FALSE; gboolean DO_RESOURCE_LIST = FALSE; gboolean DO_OPTION = FALSE; enum debug DO_DEBUG = debug_none; const char *crmd_operation = NULL; crm_data_t *msg_options = NULL; const char *admin_verbose = XML_BOOLEAN_FALSE; char *id = NULL; char *this_msg_reference = NULL; char *disconnect = NULL; char *dest_node = NULL; char *rsc_name = NULL; char *crm_option = NULL; int operation_status = 0; const char *sys_to = NULL; const char *crm_system_name = "crmadmin"; #define OPTARGS "V?K:S:HE:DW:d:i:RNst:o:" int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; ll_cluster_t *hb_cluster = NULL; - int level = 0; static struct option long_options[] = { /* Top-level Options */ {"verbose", 0, 0, 'V'}, {"help", 0, 0, '?'}, {"silent", 0, 0, 's'}, {"reference", 1, 0, 0}, {XML_ATTR_TIMEOUT, 1, 0, 't'}, /* daemon options */ {"kill", 1, 0, 'K'}, /* stop a node */ {"die", 0, 0, 0}, /* kill a node, no respawn */ {"crm_debug_inc", 1, 0, 'i'}, {"crm_debug_dec", 1, 0, 'd'}, {"status", 1, 0, 'S'}, {"health", 0, 0, 'H'}, {"election", 0, 0, 'E'}, {"dc_lookup", 0, 0, 'D'}, {"resources", 0, 0, 'R'}, {"nodes", 0, 0, 'N'}, {"whereis", 1, 0, 'W'}, {"option", 1, 0, 'o'}, {0, 0, 0, 0} }; crm_system_name = basename(argv[0]); crm_log_level = 0; crm_log_init(crm_system_name); crm_log_level = 0; if(argc < 2) { usage(crm_system_name, LSB_EXIT_EINVAL); } while (1) { flag = getopt_long(argc, argv, OPTARGS, long_options, &option_index); if (flag == -1) break; switch(flag) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); if (strcmp("reference", long_options[option_index].name) == 0) { this_msg_reference = crm_strdup(optarg); } else if (strcmp("die", long_options[option_index].name) == 0) { DO_RESET = TRUE; crmd_operation = CRM_OP_DIE; } else { printf( "?? Long option (--%s) is not yet properly supported ??\n", long_options[option_index].name); ++argerr; } break; /* a sample test for multiple instance if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); */ case 'V': - level = get_crm_log_level(); BE_VERBOSE = TRUE; admin_verbose = XML_BOOLEAN_TRUE; cl_log_enable_stderr(TRUE); - set_crm_log_level(level+1); + alter_debug(DEBUG_INC); break; case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30*1000; } break; case '?': usage(crm_system_name, LSB_EXIT_OK); break; case 'D': DO_WHOIS_DC = TRUE; break; case 'W': DO_RESOURCE = TRUE; crm_verbose("Option %c => %s", flag, optarg); rsc_name = crm_strdup(optarg); break; case 'K': DO_RESET = TRUE; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); crmd_operation = CRM_OP_LOCAL_SHUTDOWN; break; case 'o': DO_OPTION = TRUE; crm_verbose("Option %c => %s", flag, optarg); crm_option = crm_strdup(optarg); break; case 's': BE_SILENT = TRUE; break; case 'i': DO_DEBUG = debug_inc; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'd': DO_DEBUG = debug_dec; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'S': DO_HEALTH = TRUE; crm_verbose("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'E': DO_ELECT_DC = TRUE; break; case 'N': DO_NODE_LIST = TRUE; break; case 'R': DO_RESOURCE_LIST = TRUE; break; case 'H': DO_HEALTH = TRUE; 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 (argerr) { usage(crm_system_name, LSB_EXIT_GENERIC); } hb_cluster = do_init(); if (hb_cluster != NULL) { int res = do_work(hb_cluster); if (res > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); crm_verbose("%s waiting for reply from the local CRM", crm_system_name); message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); g_main_run(mainloop); return_to_orig_privs(); } else if(res == 0) { crm_verbose("%s: no reply expected", crm_system_name); } else { crm_err("No message to send"); operation_status = -1; } } else { crm_err("Init failed, could not perform requested operations"); operation_status = -2; } crm_verbose("%s exiting normally", crm_system_name); return operation_status; } int do_work(ll_cluster_t * hb_cluster) { int ret = 1; /* construct the request */ crm_data_t *msg_data = NULL; gboolean all_is_good = TRUE; msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); set_xml_property_copy(msg_options, XML_ATTR_VERBOSE, admin_verbose); set_xml_property_copy(msg_options, XML_ATTR_TIMEOUT, "0"); if (DO_HEALTH == TRUE) { crm_verbose("Querying the system"); sys_to = CRM_SYSTEM_DC; if (dest_node != NULL) { sys_to = CRM_SYSTEM_CRMD; crmd_operation = CRM_OP_PING; if (BE_VERBOSE) { expected_responses = -1;/* wait until timeout instead */ } set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); } else { crm_info("Cluster-wide health not available yet"); all_is_good = FALSE; } } else if(DO_ELECT_DC) { /* tell the local node to initiate an election */ sys_to = CRM_SYSTEM_CRMD; crmd_operation = CRM_OP_VOTE; set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; ret = 0; /* no return message */ } else if(DO_WHOIS_DC) { sys_to = CRM_SYSTEM_DC; crmd_operation = CRM_OP_PING; set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; } else if(DO_RESOURCE || DO_RESOURCE_LIST || DO_NODE_LIST || DO_OPTION){ cib_t * the_cib = cib_new(); crm_data_t *output = NULL; int call_options = cib_sync_call; enum cib_errors rc = the_cib->cmds->signon( the_cib, cib_command); if(rc != cib_ok) { return -1; } else if(DO_RESOURCE) { output = get_cib_copy(the_cib); do_find_resource(rsc_name, output); } else if(DO_RESOURCE_LIST) { output = get_cib_copy(the_cib); do_find_resource_list(output); } else if(DO_NODE_LIST) { output = get_cib_copy(the_cib); do_find_node_list(output); } else if(DO_OPTION) { char *name = NULL; char *value = NULL; crm_data_t *xml_option = NULL; crm_data_t *fragment = NULL; if(decodeNVpair(crm_option, '=', &name, &value)==FALSE){ crm_err("%s needs to be of the form" " =", crm_option); return -1; } xml_option = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); set_xml_property_copy( xml_option, XML_NVPAIR_ATTR_NAME, name); set_xml_property_copy( xml_option, XML_NVPAIR_ATTR_VALUE, value); fragment = create_cib_fragment(xml_option, NULL); free_xml(xml_option); crm_free(name); crm_free(value); rc = the_cib->cmds->modify( the_cib, XML_CIB_TAG_CRMCONFIG, fragment, NULL, call_options|cib_discard_reply); free_xml(fragment); } free_xml(output); the_cib->cmds->signoff(the_cib); return rc; } else if(DO_RESET) { /* tell dest_node to initiate the shutdown proceedure * * if dest_node is NULL, the request will be sent to the * local node */ sys_to = CRM_SYSTEM_CRMD; set_xml_property_copy( msg_options, XML_ATTR_TIMEOUT, "0"); ret = 0; /* no return message */ } else if(DO_DEBUG == debug_inc) { /* tell dest_node to increase its debug level * * if dest_node is NULL, the request will be sent to the * local node */ sys_to = CRM_SYSTEM_CRMD; crmd_operation = CRM_OP_DEBUG_UP; ret = 0; /* no return message */ } else if(DO_DEBUG == debug_dec) { /* tell dest_node to increase its debug level * * if dest_node is NULL, the request will be sent to the * local node */ sys_to = CRM_SYSTEM_CRMD; crmd_operation = CRM_OP_DEBUG_DOWN; ret = 0; /* no return message */ } else { crm_err("Unknown options"); all_is_good = FALSE; } if(all_is_good == FALSE) { crm_err("Creation of request failed. No message to send"); return -1; } /* send it */ if (crmd_channel == NULL) { crm_err("The IPC connection is not valid, cannot send anything"); return -1; } if(sys_to == NULL) { if (dest_node != NULL) sys_to = CRM_SYSTEM_CRMD; else sys_to = CRM_SYSTEM_DC; } { HA_Message *cmd = create_request( crmd_operation, msg_data, dest_node, sys_to, crm_system_name, admin_uuid); if(this_msg_reference != NULL) { ha_msg_mod(cmd, XML_ATTR_REFERENCE, this_msg_reference); } send_ipc_message(crmd_channel, cmd); } return ret; } ll_cluster_t * do_init(void) { int facility; ll_cluster_t *hb_cluster = NULL; #ifdef USE_LIBXML /* docs say only do this once, but in their code they do it every time! */ xmlInitParser (); #endif /* change the logging facility to the one used by heartbeat daemon */ hb_cluster = ll_cluster_new("heartbeat"); crm_verbose("Switching to Heartbeat logger"); if (( facility = hb_cluster->llc_ops->get_logfacility(hb_cluster)) > 0) { cl_log_set_facility(facility); } crm_malloc(admin_uuid, sizeof(char) * 11); if(admin_uuid != NULL) { snprintf(admin_uuid, 10, "%d", getpid()); admin_uuid[10] = '\0'; } init_client_ipc_comms( CRM_SYSTEM_CRMD, admin_msg_callback, NULL, &crmd_channel); if(crmd_channel != NULL) { send_hello_message( crmd_channel, admin_uuid, crm_system_name,"0", "1"); return hb_cluster; } return NULL; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { int lpc = 0; IPC_Message *msg = NULL; ha_msg_input_t *new_input = NULL; gboolean hack_return_good = TRUE; static int received_responses = 0; char *filename; int filename_len = 0; const char *result = NULL; g_source_remove(message_timer_id); while (server->ch_status != IPC_DISCONNECT && server->ops->is_message_pending(server) == TRUE) { if(new_input != NULL) { delete_ha_msg_input(new_input); } if (server->ops->recv(server, &msg) != IPC_OK) { perror("Receive failure:"); return !hack_return_good; } if (msg == NULL) { crm_trace("No message this time"); continue; } lpc++; new_input = new_ipc_msg_input(msg); msg->msg_done(msg); crm_log_message(LOG_MSG, new_input->msg); if (new_input->xml == NULL) { crm_info( "XML in IPC message was not valid... " "discarding."); continue; } else if (validate_crm_message( new_input->msg, crm_system_name, admin_uuid, XML_ATTR_RESPONSE) == FALSE) { crm_info( "Message was not a CRM response. Discarding."); continue; } result = cl_get_string(new_input->msg, XML_ATTR_RESULT); if(result == NULL || strcmp(result, "ok") == 0) { result = "pass"; } else { result = "fail"; } received_responses++; if(DO_HEALTH) { const char *state = crm_element_value( new_input->xml, "crmd_state"); printf("Status of %s@%s: %s (%s)\n", crm_element_value(new_input->xml,XML_PING_ATTR_SYSFROM), cl_get_string(new_input->msg, F_CRM_HOST_FROM), state, crm_element_value(new_input->xml,XML_PING_ATTR_STATUS)); if(BE_SILENT && state != NULL) { fprintf(stderr, "%s\n", state); } } else if(DO_WHOIS_DC) { const char *dc = cl_get_string( new_input->msg, F_CRM_HOST_FROM); printf("Designated Controller is: %s\n", dc); if(BE_SILENT && dc != NULL) { fprintf(stderr, "%s\n", dc); } } if (this_msg_reference != NULL) { /* in testing mode... */ /* 31 = "test-_.xml" + an_int_as_string + '\0' */ filename_len = 31 + strlen(this_msg_reference); crm_malloc(filename, sizeof(char) * filename_len); if(filename != NULL) { sprintf(filename, "%s-%s_%d.xml", result, this_msg_reference, received_responses); filename[filename_len - 1] = '\0'; if (0 > write_xml_file(new_input->xml, filename)) { crm_crit("Could not save response to" " %s", filename); } } } } if (server->ch_status == IPC_DISCONNECT) { crm_verbose("admin_msg_callback: received HUP"); return !hack_return_good; } if (received_responses >= expected_responses) { crm_verbose( "Recieved expected number (%d) of messages from Heartbeat." " Exiting normally.", expected_responses); g_main_quit(mainloop); return !hack_return_good; } message_timer_id = Gmain_timeout_add( message_timeout_ms, admin_message_timeout, NULL); return hack_return_good; } gboolean admin_message_timeout(gpointer data) { 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; } int do_find_resource(const char *rsc, crm_data_t *xml_node) { int found = 0; crm_data_t *nodestates = get_object_root(XML_CIB_TAG_STATUS, xml_node); const char *path2[] = { XML_CIB_TAG_LRM, XML_LRM_TAG_RESOURCES }; xml_child_iter( nodestates, a_node, XML_CIB_TAG_STATE, crm_data_t *rscstates = NULL; if(is_node_online(a_node) == FALSE) { crm_devel("Skipping offline node: %s", crm_element_value(a_node, XML_ATTR_ID)); continue; } rscstates = find_xml_node_nested(a_node, path2, DIMOF(path2)); xml_child_iter( rscstates, rsc_state, XML_LRM_TAG_RESOURCE, const char *id = crm_element_value(rsc_state,XML_ATTR_ID); const char *target = crm_element_value(rsc_state,XML_LRM_ATTR_TARGET); const char *last_op = crm_element_value(rsc_state,XML_LRM_ATTR_LASTOP); const char *op_code = crm_element_value(rsc_state,XML_LRM_ATTR_OPSTATUS); crm_devel("checking %s:%s for %s", target, id, rsc); if(safe_str_neq(rsc, id)){ crm_trace("no match"); continue; } if(safe_str_eq("stop", last_op)) { crm_devel("resource %s is stopped on: %s\n", rsc, target); } else if(safe_str_eq(op_code, "-1")) { crm_devel("resource %s is pending on: %s\n", rsc, target); } else if(safe_str_neq(op_code, "0")) { crm_devel("resource %s is failed on: %s\n", rsc, target); } else { crm_devel("resource %s is running on: %s\n", rsc, target); printf("resource %s is running on: %s\n", rsc, target); if(BE_SILENT) { fprintf(stderr, "%s ", target); } found++; } ); if(BE_SILENT) { fprintf(stderr, "\n"); } ); if(found == 0) { printf("resource %s is NOT running\n", rsc); } return found; } gboolean is_node_online(crm_data_t *node_state) { const char *uname = crm_element_value(node_state,XML_ATTR_UNAME); const char *join_state = crm_element_value(node_state,XML_CIB_ATTR_JOINSTATE); const char *crm_state = crm_element_value(node_state,XML_CIB_ATTR_CRMDSTATE); const char *ha_state = crm_element_value(node_state,XML_CIB_ATTR_HASTATE); const char *ccm_state = crm_element_value(node_state,XML_CIB_ATTR_INCCM); if(safe_str_eq(join_state, CRMD_JOINSTATE_MEMBER) && safe_str_eq(ha_state, ACTIVESTATUS) && safe_str_eq(ccm_state, XML_BOOLEAN_YES) && safe_str_eq(crm_state, ONLINESTATUS)) { crm_devel("Node %s is online", uname); return TRUE; } crm_devel("Node %s: %s %s %s", uname, join_state, ccm_state, crm_state); crm_devel("Node %s is offline", uname); return FALSE; } int do_find_resource_list(crm_data_t *xml_node) { int found = 0; crm_data_t *rscs = get_object_root(XML_CIB_TAG_RESOURCES, xml_node); xml_child_iter( rscs, rsc, XML_CIB_TAG_RESOURCE, printf("%s resource: %s (%s)\n", crm_element_value(rsc, "class"), crm_element_value(rsc, XML_ATTR_ID), crm_element_value(rsc, XML_ATTR_TYPE)); found++; ); if(found == 0) { printf("NO resources configured\n"); } return found; } int do_find_node_list(crm_data_t *xml_node) { int found = 0; crm_data_t *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); xml_child_iter( nodes, node, XML_CIB_TAG_NODE, printf("%s node: %s (%s)\n", crm_element_value(node, XML_ATTR_TYPE), crm_element_value(node, XML_ATTR_UNAME), crm_element_value(node, XML_ATTR_ID)); found++; ); if(found == 0) { printf("NO nodes configured\n"); } return found; } void usage(const char *cmd, int exit_status) { FILE *stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-?vs] [command] [command args]\n", cmd); fprintf(stream, "Options\n"); fprintf(stream, "\t--%s (-%c)\t: " "turn on debug info. additional instances increase verbosity\n", "verbose", 'V'); fprintf(stream, "\t--%s (-%c)\t: be very very quiet\n", "silent", 's'); fprintf(stream, "\t--%s (-%c)\t: this help message\n", "help", '?'); fprintf(stream, "\nCommands\n"); fprintf(stream, "\t--%s (-%c) \t: " "increment the CRMd debug level on \n", CRM_OP_DEBUG_UP,'i'); fprintf(stream, "\t--%s (-%c) \t: " "decrement the CRMd debug level on \n", CRM_OP_DEBUG_DOWN,'d'); fprintf(stream, "\t--%s (-%c) \t: " "shutdown the CRMd on \n", "kill", 'K'); fprintf(stream, "\t--%s (-%c) \t: " "request the status of \n", "status", 'S'); fprintf(stream, "\t--%s (-%c)\t\t: " "request the status of all nodes\n", "health", 'H'); fprintf(stream, "\t--%s (-%c) \t: " "initiate an election from \n", "election", 'E'); fprintf(stream, "\t--%s (-%c)\t: " "request the uname of the DC\n", "dc_lookup", 'D'); fprintf(stream, "\t--%s (-%c)\t\t: " "request the uname of all member nodes\n", "nodes", 'N'); fprintf(stream, "\t--%s (-%c)\t: " "request the names of all resources\n", "resources", 'R'); fprintf(stream, "\t--%s (-%c) \t: " "request the location of \n", "whereis", 'W'); /* fprintf(stream, "\t--%s (-%c)\t\n", "disconnect", 'D'); */ fflush(stream); exit(exit_status); }