diff --git a/tools/Makefile.am b/tools/Makefile.am index 1c6820fffe..4facc5a302 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,120 +1,121 @@ # # Copyright (C) 2004-2009 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 ccdv SUBDIRS = shell INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(AISPREFIX)/include/openais COMMONLIBS = \ $(top_builddir)/lib/common/libcrmcommon.la \ $(top_builddir)/lib/cib/libcib.la \ -lplumb $(CURSESLIBS) $(CLUSTERLIBS) headerdir = $(pkgincludedir)/crm header_HEADERS = attrd.h EXTRA_DIST = ccdv.c halibdir = $(CRM_DAEMON_DIR) halib_SCRIPTS = haresources2cib.py hb2openais.sh halib_PROGRAMS = attrd pingd halib_PYTHON = crm_primitive.py hb2openais-helper.py sbin_PROGRAMS = crmadmin cibadmin crm_node crm_attribute crm_resource crm_verify \ crm_uuid crm_shadow attrd_updater crm_diff crm_mon iso8601 if BUILD_HELP man8_MANS = $(sbin_PROGRAMS:%=%.8) %.8: % + $(abs_builddir)/$< --help help2man --output $@ --no-info --section 8 --name "Part of the Pacemaker cluster resource manager" $(abs_builddir)/$< endif sbin_SCRIPTS = crm crm_standby crm_master crm_failcount ## SOURCES ccdv: $(top_srcdir)/tools/ccdv.c gcc $(AM_CFLAGS) $(CFLAGS) -o ccdv $(top_srcdir)/tools/ccdv.c ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(COMMONLIBS) $(CLUSTERLIBS) \ $(top_builddir)/lib/pengine/libpe_status.la crm_uuid_SOURCES = crm_uuid.c crm_uuid_LDADD = $(top_builddir)/lib/common/libcrmcluster.la cibadmin_SOURCES = cibadmin.c cibadmin_LDADD = $(COMMONLIBS) crm_shadow_SOURCES = cib_shadow.c crm_shadow_LDADD = $(COMMONLIBS) crm_node_SOURCES = ccm_epoche.c crm_node_LDADD = $(COMMONLIBS) $(CLUSTERLIBS) \ $(top_builddir)/lib/common/libcrmcluster.la crm_diff_SOURCES = xml_diff.c crm_diff_LDADD = $(COMMONLIBS) crm_mon_SOURCES = crm_mon.c crm_mon_LDADD = $(COMMONLIBS) $(SNMPLIBS) $(ESMTPLIBS) -llrm \ $(top_builddir)/lib/pengine/libpe_status.la # Arguments could be made that this should live in crm/pengine crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(COMMONLIBS) crm_resource_SOURCES = crm_resource.c crm_resource_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la iso8601_SOURCES = test.iso8601.c iso8601_LDADD = $(COMMONLIBS) # A little trick. Now ccdv can be auto-built but not auto-cleaned. attrd_DEPENDENCIES = ccdv attrd_SOURCES = attrd.c attrd_LDADD = $(COMMONLIBS) $(top_builddir)/lib/common/libcrmcluster.la pingd_SOURCES = pingd.c pingd_LDADD = $(COMMONLIBS) attrd_updater_SOURCES = attrd_updater.c attrd_updater_LDADD = $(COMMONLIBS) clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: .PHONY: install-exec-hook diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c index 03ff2fbad6..fd3dd4ad28 100644 --- a/tools/attrd_updater.c +++ b/tools/attrd_updater.c @@ -1,140 +1,141 @@ /* * 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 const char *attr_name = NULL; const char *attr_value = NULL; const char *attr_set = NULL; const char *attr_section = NULL; const char *attr_dampen = NULL; char command = 'q'; static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, {"name", 1, 0, 'n', "The attribute's name"}, - {"-spacer-",1, 0, '-', "\nCommands:\n"}, + {"-spacer-",1, 0, '-', "\nCommands:"}, {"update", 1, 0, 'U', "Update the attribute's value in attrd. If this causes the value to change, it will also be updated in the cluster configuration"}, {"query", 0, 0, 'Q', "\tQuery the attribute's value from attrd"}, {"delete", 0, 0, 'D', "\tDelete the attribute in attrd. If a value was previously set, it will also be removed from the cluster configuration"}, {"refresh", 0, 0, 'R', "\t(Advanced) Force the attrd daemon to resend all current values to the CIB\n"}, - {"-spacer-",1, 0, '-', "\nAdditional options:\n"}, + {"-spacer-",1, 0, '-', "\nAdditional options:"}, + {"lifetime",1, 0, 'l', "Lifetime of the node attribute. Allowed values: forever, reboot"}, {"delay", 1, 0, 'd', "The time to wait (dampening) in seconds further changes occur"}, {"set", 1, 0, 's', "(Advanced) The attribute set in which to place the value"}, - {"section", 1, 0, 'S', "(Advanced) The section in which to place the value\n"}, - /* Legacy options */ {"update", 1, 0, 'v', NULL, 1}, + {"section", 1, 0, 'S', NULL, 1}, {0, 0, 0, 0} }; int main(int argc, char ** argv) { int index = 0; int argerr = 0; int flag; crm_log_init("attrd_updater", LOG_ERR, FALSE, FALSE, argc, argv); - crm_set_options("?$Vn:v:d:s:S:RDQU:", "{command} -n {attribute} [options]", long_options, "Tool for updating cluster node attributes"); + crm_set_options("?$Vn:v:d:s:S:RDQU:l:", "command -n attribute [options]", long_options, "Tool for updating cluster node attributes"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch(flag) { case 'V': alter_debug(DEBUG_INC); break; case '?': case '$': crm_help(flag, LSB_EXIT_OK); break; case 'n': attr_name = crm_strdup(optarg); break; case 's': attr_set = crm_strdup(optarg); break; case 'd': attr_dampen = crm_strdup(optarg); break; + case 'l': case 'S': attr_section = crm_strdup(optarg); break; case 'q': case 'R': case 'D': case 'U': case 'v': command = flag; attr_value = optarg; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if(command != 'R' && attr_name == NULL) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(command == 'q') { fprintf(stderr, "-q,--query is not yet implemented, use -D to delete existing values\n\n"); crm_help('?', LSB_EXIT_GENERIC); } else if(attrd_lazy_update(command, NULL, attr_name, attr_value, attr_section, attr_set, attr_dampen) == FALSE) { fprintf(stderr, "Could not update %s=%s\n", attr_name, attr_value); return 1; } return 0; } diff --git a/tools/ccm_epoche.c b/tools/ccm_epoche.c index 0fc8938014..3a45dc9fdb 100644 --- a/tools/ccm_epoche.c +++ b/tools/ccm_epoche.c @@ -1,368 +1,368 @@ /* * 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 /* for basename() */ #include #include #include #include int command = 0; int ccm_fd = 0; int try_hb = 1; int try_ais = 1; gboolean do_quiet = FALSE; char *target_uuid = NULL; char *target_uname = NULL; const char *standby_value = NULL; const char *standby_scope = NULL; void ais_membership_destroy(gpointer user_data); gboolean ais_membership_dispatch(AIS_Message *wrapper, char *data, int sender); #include <../lib/common/stack.h> #ifdef SUPPORT_HEARTBEAT # include # include oc_ev_t *ccm_token = NULL; void oc_ev_special(const oc_ev_t *, oc_ev_class_t , int ); void ccm_age_callback( oc_ed_t event, void *cookie, size_t size, const void *data); gboolean ccm_age_connect(int *ccm_fd); #endif static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output"}, - {"quiet", 0, 0, 'Q', "Essential output only"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, + {"quiet", 0, 0, 'Q', "\tEssential output only"}, {"-spacer-", 1, 0, '-', "\nStack:", SUPPORT_HEARTBEAT}, {"openais", 0, 0, 'A', "\tOnly try connecting to an OpenAIS-based cluster", SUPPORT_HEARTBEAT}, {"heartbeat", 0, 0, 'H', "Only try connecting to a Heartbeat-based cluster", SUPPORT_HEARTBEAT}, {"-spacer-", 1, 0, '-', "\nCommands:"}, {"epoch", 0, 0, 'e', "\tDisplay the epoch during which this node joined the cluster"}, {"quorum", 0, 0, 'q', "\tDisplay a 1 if our partition has quorum, 0 if not"}, {"partition", 0, 0, 'p', "Display the members of this partition"}, {"remove", 1, 0, 'R', "(Advanced, AIS-Only) Remove the (stopped) node with the specified nodeid from the cluster"}, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"force", 0, 0, 'f'}, {0, 0, 0, 0} }; int main(int argc, char ** argv) { int flag = 0; int argerr = 0; gboolean force_flag = FALSE; gboolean dangerous_cmd = FALSE; int option_index = 0; crm_peer_init(); crm_log_init(basename(argv[0]), LOG_WARNING, FALSE, FALSE, argc, argv); - crm_set_options("?V$qepHR:s:SN:l:", "[-?$V] {command} [options]", long_options, - "Tool for displaying node-level information"); + crm_set_options("?V$qepHR:s:SN:l:", "command [options]", long_options, + "Tool for displaying low-level node information"); while (flag >= 0) { flag = crm_get_option(argc, argv, &option_index); switch(flag) { case -1: break; case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'Q': do_quiet = TRUE; break; case 'H': try_ais = 0; break; case 'A': try_hb = 0; break; case 'f': force_flag = TRUE; break; case 'R': dangerous_cmd = TRUE; command = flag; target_uname = optarg; break; case 'p': case 'e': case 'q': command = flag; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); exit(LSB_EXIT_GENERIC); } #if SUPPORT_AIS if(try_ais && init_ais_connection( ais_membership_dispatch, ais_membership_destroy, NULL, NULL, NULL)) { GMainLoop* amainloop = NULL; switch(command) { case 'R': send_ais_text(crm_class_rmpeer, target_uname, TRUE, NULL, crm_msg_ais); return 0; case 'e': /* Age makes no sense (yet) in an AIS cluster */ fprintf(stdout, "1\n"); return 0; case 'q': send_ais_text(crm_class_quorum, NULL, TRUE, NULL, crm_msg_ais); break; case 'p': crm_info("Requesting the list of configured nodes"); send_ais_text(crm_class_members, __FUNCTION__, TRUE, NULL, crm_msg_ais); break; default: fprintf(stderr, "Unknown option '%c'\n", command); crm_help('?', LSB_EXIT_GENERIC); } amainloop = g_main_new(FALSE); g_main_run(amainloop); } #endif #if SUPPORT_HEARTBEAT if(try_hb && ccm_age_connect(&ccm_fd)) { int rc = 0; fd_set rset; oc_ev_t *ccm_token = NULL; while (1) { sleep(1); FD_ZERO(&rset); FD_SET(ccm_fd, &rset); errno = 0; rc = select(ccm_fd + 1, &rset, NULL,NULL,NULL); if(rc > 0 && oc_ev_handle_event(ccm_token) != 0) { crm_err("oc_ev_handle_event failed"); return 1; } else if(rc < 0 && errno != EINTR) { crm_perror(LOG_ERR, "select failed"); return 1; } } } #endif return(1); } #if SUPPORT_HEARTBEAT 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; } 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; node_list_size = oc->m_n_member; if(command == 'q') { crm_debug("Processing \"%s\" event.", event==OC_EV_MS_NEW_MEMBERSHIP?"NEW MEMBERSHIP": event==OC_EV_MS_NOT_PRIMARY?"NOT PRIMARY": event==OC_EV_MS_PRIMARY_RESTORED?"PRIMARY RESTORED": event==OC_EV_MS_EVICTED?"EVICTED": "NO QUORUM MEMBERSHIP"); if(ccm_have_quorum(event)) { fprintf(stdout, "1\n"); } else { fprintf(stdout, "0\n"); } } else if(command == 'e') { crm_debug("Searching %d members for our birth", oc->m_n_member); } for(lpc=0; lpcm_array[oc->m_memb_idx+lpc].node_uname); } else if(command == 'e') { 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); } } } oc_ev_callback_done(cookie); if(command == 'p') { fprintf(stdout, "\n"); } fflush(stdout); exit(0); } #endif #if SUPPORT_AIS void ais_membership_destroy(gpointer user_data) { crm_err("AIS connection terminated"); ais_fd_sync = -1; exit(1); } #endif static gint member_sort(gconstpointer a, gconstpointer b) { const crm_node_t *node_a = a; const crm_node_t *node_b = b; return strcmp(node_a->uname, node_b->uname); } static void crm_add_member( gpointer key, gpointer value, gpointer user_data) { GList **list = user_data; crm_node_t *node = value; if(node->uname != NULL) { *list = g_list_insert_sorted(*list, node, member_sort); } } gboolean ais_membership_dispatch(AIS_Message *wrapper, char *data, int sender) { switch(wrapper->header.id) { case crm_class_members: case crm_class_notify: case crm_class_quorum: break; default: return TRUE; break; } if(command == 'q') { if(crm_have_quorum) { fprintf(stdout, "1\n"); } else { fprintf(stdout, "0\n"); } } else if(command == 'p') { GList *nodes = NULL; g_hash_table_foreach(crm_peer_cache, crm_add_member, &nodes); slist_iter(node, crm_node_t, nodes, lpc, if(node->uname && crm_is_member_active(node)) { fprintf(stdout, "%s ", node->uname); } ); fprintf(stdout, "\n"); } exit(0); return TRUE; } diff --git a/tools/cib_shadow.c b/tools/cib_shadow.c index a0b3b1b5c1..7c883bd587 100644 --- a/tools/cib_shadow.c +++ b/tools/cib_shadow.c @@ -1,542 +1,544 @@ /* * 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 int exit_code = cib_ok; GMainLoop *mainloop = NULL; IPC_Channel *crmd_channel = NULL; const char *host = NULL; void usage(const char *cmd, int exit_status); int command_options = cib_sync_call; const char *cib_action = NULL; cib_t *real_cib = NULL; int dump_data_element( int depth, char **buffer, int *max, int *offset, const char *prefix, xmlNode *data, gboolean formatted); void print_xml_diff(FILE *where, xmlNode *diff); static int force_flag = 0; static int batch_flag = 0; static char *get_shadow_prompt(const char *name) { int len = 16; char *prompt = NULL; CRM_ASSERT(name != NULL); len += strlen(name); crm_malloc0(prompt, len); snprintf(prompt, len, "shadow[%s] # ", name); return prompt; } static void shadow_setup(char *name, gboolean do_switch) { const char *prompt = getenv("PS1"); const char *shell = getenv("SHELL"); char *new_prompt = get_shadow_prompt(name); printf("Setting up shadow instance\n"); if(safe_str_eq(new_prompt, prompt)) { /* nothing to do */ goto done; } else if(batch_flag == FALSE && shell != NULL) { setenv("PS1", new_prompt, 1); setenv("CIB_shadow", name, 1); printf("Type Ctrl-D to exit the crm_shadow shell\n"); execl(shell, "--norc", "--noprofile", NULL); } else if (do_switch) { printf("To switch to the named shadow instance, paste the following into your shell:\n"); } else { printf("A new shadow instance was created. To begin using it paste the following into your shell:\n"); } printf(" CIB_shadow=%s ; export CIB_shadow\n", name); done: crm_free(new_prompt); } static void shadow_teardown(char *name) { const char *prompt = getenv("PS1"); char *our_prompt = get_shadow_prompt(name); if(prompt != NULL && strstr(prompt, our_prompt)) { printf("Now type Ctrl-D to exit the crm_shadow shell\n"); } else { printf("Please remember to unset the CIB_shadow variable by pasting the following into your shell:\n"); printf(" unset CIB_shadow\n"); } crm_free(our_prompt); } static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, + {"help", 0, 0, '?', "\t\tThis text"}, + {"version", 0, 0, '$', "\t\tVersion information" }, + {"verbose", 0, 0, 'V', "\t\tIncrease debug output"}, - {"create", required_argument, NULL, 'c', "\tCreate the named shadow copy of the active cluster configuration"}, - {"create-empty", required_argument, NULL, 'e', "Create the named shadow copy with an empty cluster configuration\n"}, - - {"which", no_argument, NULL, 'w', "\tIndicate the active shadow copy"}, - {"display", no_argument, NULL, 'p', "\tDisplay the contents of the shadow copy"}, - {"edit", no_argument, NULL, 'E', "\tEdit the contents of the named shadow copy with your favorite $EDITOR"}, - {"diff", no_argument, NULL, 'd', "\tDisplay the changes in the shadow copy\n"}, + {"-spacer-", 1, 0, '-', "\nQueries:"}, + {"which", no_argument, NULL, 'w', "\t\tIndicate the active shadow copy"}, + {"display", no_argument, NULL, 'p', "\t\tDisplay the contents of the shadow copy"}, + {"edit", no_argument, NULL, 'E', "\t\tEdit the contents of the named shadow copy with your favorite $EDITOR"}, + {"diff", no_argument, NULL, 'd', "\t\tDisplay the changes in the shadow copy\n"}, - {"commit", required_argument, NULL, 'C', "Upload the contents of the named shadow copy to the cluster"}, - {"delete", required_argument, NULL, 'D', "Delete the contents of the named shadow copy"}, - {"reset", required_argument, NULL, 'r', "Recreate the named shadow copy from the active cluster configuration\n"}, + {"-spacer-", 1, 0, '-', "\nCommands:"}, + {"create", required_argument, NULL, 'c', "\tCreate the named shadow copy of the active cluster configuration"}, + {"create-empty", required_argument, NULL, 'e', "Create the named shadow copy with an empty cluster configuration"}, + {"commit", required_argument, NULL, 'C', "\tUpload the contents of the named shadow copy to the cluster"}, + {"delete", required_argument, NULL, 'D', "\tDelete the contents of the named shadow copy"}, + {"reset", required_argument, NULL, 'r', "\tRecreate the named shadow copy from the active cluster configuration"}, + {"switch", required_argument, NULL, 's', "\t(Advanced) Switch to the named shadow copy"}, - {"force", no_argument, NULL, 'f', "\t(Advanced) Force the action to be performed"}, - {"batch", no_argument, NULL, 'b', "\t(Advanced) Don't spawn a new shell" }, - {"switch", required_argument, NULL, 's', "(Advanced) Switch to the named shadow copy"}, + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {"force", no_argument, NULL, 'f', "\t\t(Advanced) Force the action to be performed"}, + {"batch", no_argument, NULL, 'b', "\t\t(Advanced) Don't spawn a new shell" }, {0, 0, 0, 0} }; int main(int argc, char **argv) { int rc = 0; int flag; int argerr = 0; static int command = '?'; char *shadow = NULL; char *shadow_file = NULL; gboolean dangerous_cmd = FALSE; struct stat buf; int option_index = 0; crm_log_init("crm_shadow", LOG_CRIT, FALSE, FALSE, argc, argv); - crm_set_options("V$?bfwc:dr:C:D:ps:Ee:", NULL, long_options, + crm_set_options("V$?bfwc:dr:C:D:ps:Ee:", "(query|command) [modifiers]", long_options, "Perform configuration changes in a sandbox before updating the live cluster.\n" " Sets up an environment in which configuration tools (cibadmin, crm_resource, etc) work" " offline instead of against a live cluster, allowing changes to be previewed and tested" " for side-effects.\n"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1 || flag == 0) break; switch(flag) { case 'd': case 'E': case 'p': case 'w': command = flag; shadow = crm_strdup(getenv("CIB_shadow")); break; case 'e': case 'c': case 's': case 'r': command = flag; shadow = crm_strdup(optarg); break; case 'C': case 'D': command = flag; dangerous_cmd = TRUE; shadow = crm_strdup(optarg); break; case 'V': command_options = command_options | cib_verbose; cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'f': command_options |= cib_quorum_override; force_flag = 1; break; case 'b': batch_flag = 1; 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"); crm_help('?', LSB_EXIT_EINVAL); } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(command == 'w') { /* which shadow instance is active? */ const char *local = getenv("CIB_shadow"); if(local == NULL) { fprintf(stderr, "No shadow instance provided\n"); return cib_NOTEXISTS; } fprintf(stdout, "%s\n", local); return 0; } if(shadow == NULL) { fprintf(stderr, "No shadow instance provided\n"); fflush(stderr); return CIBRES_MISSING_FIELD; } else if(command != 's' && command != 'c') { const char *local = getenv("CIB_shadow"); if(local != NULL && safe_str_neq(local, shadow) && force_flag == FALSE) { fprintf(stderr, "The supplied shadow instance (%s) is not the same as the active one (%s).\n" " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n", shadow, local); fflush(stderr); exit(LSB_EXIT_GENERIC); } } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); exit(LSB_EXIT_GENERIC); } shadow_file = get_shadow_file(shadow); if(command == 'D') { /* delete the file */ rc = stat(shadow_file, &buf); if(rc == 0) { rc = unlink(shadow_file); if(rc != 0) { fprintf(stderr, "Could not remove shadow instance '%s': %s\n", shadow, strerror(errno)); return rc; } } shadow_teardown(shadow); return rc; } if(command == 'd' || command == 'r' || command == 'c' || command == 'C') { real_cib = cib_new_no_shadow(); rc = real_cib->cmds->signon(real_cib, crm_system_name, cib_command); if(rc != cib_ok) { fprintf(stderr, "Signon to CIB failed: %s\n", cib_error2string(rc)); return rc; } } rc = stat(shadow_file, &buf); if(command == 'e' || command == 'c') { if (rc == 0 && force_flag == FALSE) { fprintf(stderr, "A shadow instance '%s' already exists.\n" " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n", shadow); return cib_EXISTS; } } else if(rc != 0) { fprintf(stderr, "Could not access shadow instance '%s': %s\n", shadow, strerror(errno)); return cib_NOTEXISTS; } rc = cib_ok; if(command == 'c' || command == 'e') { xmlNode *output = NULL; /* create a shadow instance based on the current cluster config */ if(command == 'c') { rc = real_cib->cmds->query(real_cib, NULL, &output, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not connect to the CIB: %s\n", cib_error2string(rc)); return rc; } } else { output = createEmptyCib(); crm_xml_add(output, XML_ATTR_GENERATION, "0"); crm_xml_add(output, XML_ATTR_NUMUPDATES, "0"); crm_xml_add(output, XML_ATTR_GENERATION_ADMIN, "0"); crm_xml_add(output, XML_ATTR_VALIDATION, LATEST_SCHEMA_VERSION); } rc = write_xml_file(output, shadow_file, FALSE); if(rc < 0) { fprintf(stderr, "Could not create the shadow instance '%s': %s\n", shadow, strerror(errno)); return rc; } shadow_setup(shadow, FALSE); rc = cib_ok; } else if(command == 'E') { const char *err = NULL; char *editor = getenv("EDITOR"); if(editor == NULL) { fprintf(stderr, "No value for $EDITOR defined\n"); return cib_missing; } execlp(editor, "--", shadow_file, NULL); err = strerror(errno); fprintf(stderr, "Could not invoke $EDITOR (%s %s)\n", editor, shadow_file); return cib_missing; } else if(command == 's') { shadow_setup(shadow, TRUE); return 0; } else if(command == 'P') { /* display the current contents */ char *output_s = NULL; xmlNode *output = filename2xml(shadow_file); output_s = dump_xml_formatted(output); printf("%s", output_s); crm_free(output_s); free_xml(output); } else if(command == 'd') { /* diff against cluster */ xmlNode *diff = NULL; xmlNode *old_config = NULL; xmlNode *new_config = filename2xml(shadow_file); rc = real_cib->cmds->query(real_cib, NULL, &old_config, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not query the CIB: %s\n", cib_error2string(rc)); return rc; } diff = diff_xml_object(old_config, new_config, FALSE); if(diff != NULL) { print_xml_diff(stderr, diff); return 1; } return 0; } else if(command == 'C') { /* commit to the cluster */ xmlNode *input = filename2xml(shadow_file); rc = real_cib->cmds->replace(real_cib, NULL, input, command_options); if(rc != cib_ok) { fprintf(stderr, "Could not commit shadow instance '%s' to the CIB: %s\n", shadow, cib_error2string(rc)); return rc; } shadow_teardown(shadow); } return rc; } #define bhead(buffer, offset) ((*buffer) + (*offset)) #define bremain(max, offset) ((*max) - (*offset)) #define update_buffer_head(len) do { \ int total = (*offset) + len + 1; \ if(total >= (*max)) { /* too late */ \ (*buffer) = EOS; return -1; \ } else if(((*max) - total) < 256) { \ (*max) *= 10; \ crm_realloc(*buffer, (*max)); \ } \ (*offset) += len; \ } while(0) extern int print_spaces(char *buffer, int depth, int max); int dump_data_element( int depth, char **buffer, int *max, int *offset, const char *prefix, xmlNode *data, gboolean formatted) { int printed = 0; int has_children = 0; const char *name = NULL; CRM_CHECK(data != NULL, return 0); name = crm_element_name(data); CRM_CHECK(name != NULL, return 0); CRM_CHECK(buffer != NULL && *buffer != NULL, return 0); crm_debug_5("Dumping %s...", name); if(prefix) { printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", prefix); update_buffer_head(printed); } if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "<%s", name); update_buffer_head(printed); xml_prop_iter(data, prop_name, prop_value, crm_debug_5("Dumping <%s %s=\"%s\"...", name, prop_name, prop_value); printed = snprintf(bhead(buffer, offset), bremain(max, offset), " %s=\"%s\"", prop_name, prop_value); update_buffer_head(printed); ); has_children = xml_has_children(data); printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s>%s", has_children==0?"/":"", formatted?"\n":""); update_buffer_head(printed); if(has_children == 0) { return 0; } xml_child_iter(data, child, if(dump_data_element(depth+1, buffer, max, offset, prefix, child, formatted) < 0) { return -1; } ); if(prefix) { printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", prefix); update_buffer_head(printed); } if(formatted) { printed = print_spaces(bhead(buffer, offset), depth, bremain(max, offset)); update_buffer_head(printed); } printed = snprintf(bhead(buffer, offset), bremain(max, offset), "%s", name, formatted?"\n":""); update_buffer_head(printed); crm_debug_5("Dumped %s...", name); return has_children; } void print_xml_diff(FILE *where, xmlNode *diff) { char *buffer = NULL; int max = 1024, len = 0; gboolean is_first = TRUE; xmlNode *added = find_xml_node(diff, "diff-added", FALSE); xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE); is_first = TRUE; xml_child_iter( removed, child, len = 0; max = 1024; crm_free(buffer); crm_malloc0(buffer, max); if(is_first) { is_first = FALSE; } else { fprintf(where, " --- \n"); } CRM_CHECK(dump_data_element( 0, &buffer, &max, &len, "-", child, TRUE) >= 0, continue); fprintf(where, "%s", buffer); ); is_first = TRUE; xml_child_iter( added, child, len = 0; max = 1024; crm_free(buffer); crm_malloc0(buffer, max); if(is_first) { is_first = FALSE; } else { fprintf(where, " +++ \n"); } CRM_CHECK(dump_data_element( 0, &buffer, &max, &len, "+", child, TRUE) >= 0, continue); fprintf(where, "%s", buffer); ); } diff --git a/tools/cibadmin.c b/tools/cibadmin.c index ed113d7c82..f353403a35 100644 --- a/tools/cibadmin.c +++ b/tools/cibadmin.c @@ -1,514 +1,514 @@ /* * 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 int exit_code = cib_ok; int message_timer_id = -1; int message_timeout_ms = 30; GMainLoop *mainloop = NULL; 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(xmlNode *input, int command_options, xmlNode **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(xmlNode *msg, int call_id, int rc, xmlNode *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 *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; gboolean force_flag = FALSE; static struct crm_option long_options[] = { - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, {"-spacer-", 0, 0, '-', "Commands:"}, {"upgrade", 0, 0, 'u', "\tUpgrade the configuration to the latest syntax"}, {"query", 0, 0, 'Q', "\tQuery the contents of the CIB"}, {"erase", 0, 0, 'E', "\tErase the contents of the whole CIB"}, + {"bump", 0, 0, 'B', "\tIncrease the CIB's epoch value by 1"}, {"create", 0, 0, 'C', "\tCreate an object in the CIB. Will fail if the object already exists."}, {"modify", 0, 0, 'M', "\tFind the object somewhere in the CIB's XML tree and update it. Fails if the object does not exist unless -c is specified"}, + {"patch", 0, 0, 'P', "\tSupply an update in the form of an xml diff (See also: crm_diff)"}, {"replace", 0, 0, 'R', "\tRecursivly replace an object in the CIB"}, {"delete", 0, 0, 'D', "\tDelete the first object matching the supplied criteria, Eg. "}, - {"-spacer-", 0, 0, '-', "\t\t\tThe tagname and all attributes must match in order for the element to be deleted"}, + {"-spacer-", 0, 0, '-', "\n\t\t\tThe tagname and all attributes must match in order for the element to be deleted"}, {"md5-sum", 0, 0, '5', "\tCalculate a CIB digest"}, - {"patch", 0, 0, 'P', "\tSupply an update in the form of an xml diff (as produced by crm_diff)"}, - {"bump", 0, 0, 'B', "\tIncrease the CIB's epoch value by 1"}, - {"sync", 0, 0, 'S', "\tForce a refresh of the CIB to all nodes\n"}, + {"sync", 0, 0, 'S', "\t(Advanced) Force a refresh of the CIB to all nodes\n"}, {"make-slave", 0, 0, 'r', NULL, 1}, {"make-master", 0, 0, 'w', NULL, 1}, {"is-master", 0, 0, 'm', NULL, 1}, - {"-spacer-", 0, 0, '-', "Command Modifiers:"}, + {"-spacer-",1, 0, '-', "\nAdditional options:"}, {"force", 0, 0, 'f'}, {"timeout", 1, 0, 't', "Time (in seconds) to wait before declaring the operation failed"}, {"sync-call", 0, 0, 's', "Wait for call to complete before returning"}, {"local", 0, 0, 'l', "\tCommand takes effect locally. Should only be used for queries"}, {"allow-create",0, 0, 'c', "(Advanced) Allow the target of a -M operation to be created if they do not exist"}, {"no-children", 0, 0, 'n', "(Advanced) When querying an object, do not return include its children in the result\n"}, {"no-bcast", 0, 0, 'b', NULL, 1}, - {"-spacer-", 0, 0, '-', "Command Data:"}, + {"-spacer-", 0, 0, '-', "Data:"}, {"xml-text", 1, 0, 'X', "Retrieve XML from the supplied string"}, {"xml-file", 1, 0, 'x', "Retrieve XML from the named file"}, {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin\n"}, {"xpath", 1, 0, 'A', "A valid XPath to use instead of -o"}, {"obj_type", 1, 0, 'o', "Limit the scope of the operation to a specific section of the CIB."}, {"-spacer-", 0, 0, '-', "\t\t\tValid values are: nodes, resources, constraints, crm_config, rsc_defaults, op_defaults, status"}, - {"node", 1, 0, 'N', "\t(Advanced) Send command to the specified host\n"}, + {"node", 1, 0, 'N', "(Advanced) Send command to the specified host\n"}, {"-spacer-", 0, 0, '-', "\nExamples:\n"}, {"-spacer-", 0, 0, '-', "cibadmin --query --local\t\t\t\tQuery the configuration from the local node\n"}, {"-spacer-", 0, 0, '-', "cibadmin --delete --xml-text ''\tRemove the resource named 'old'\n"}, /* Legacy options */ {"host", 0, 0, 'h', NULL, 1}, {"force-quorum", 0, 0, 'f', NULL, 1}, {F_CRM_DATA, 1, 0, 'X', NULL, 1}, {CIB_OP_ERASE, 0, 0, 'E', NULL, 1}, {CIB_OP_QUERY, 0, 0, 'Q', NULL, 1}, {CIB_OP_CREATE, 0, 0, 'C', NULL, 1}, {CIB_OP_REPLACE, 0, 0, 'R', NULL, 1}, {CIB_OP_UPDATE, 0, 0, 'U', NULL, 1}, {CIB_OP_MODIFY, 0, 0, 'M', NULL, 1}, {CIB_OP_DELETE, 0, 0, 'D', NULL, 1}, {CIB_OP_BUMP, 0, 0, 'B', NULL, 1}, {CIB_OP_SYNC, 0, 0, 'S', NULL, 1}, {CIB_OP_SLAVE, 0, 0, 'r', NULL, 1}, {CIB_OP_MASTER, 0, 0, 'w', NULL, 1}, {CIB_OP_ISMASTER,0, 0, 'm', NULL, 1}, {0, 0, 0, 0} }; int main(int argc, char **argv) { int argerr = 0; int flag; const char *source = NULL; char *admin_input_xml = NULL; char *admin_input_file = NULL; gboolean dangerous_cmd = FALSE; gboolean admin_input_stdin = FALSE; xmlNode *output = NULL; xmlNode *input = NULL; int option_index = 0; crm_log_init("cibadmin", LOG_CRIT, FALSE, FALSE, argc, argv); - crm_set_options("V?$o:QDUCEX:t:Srwlsh:MmBfbRx:pP5N:A:unc", "command [modifiers] [data]", long_options, + crm_set_options("V?$o:QDUCEX:t:Srwlsh:MmBfbRx:pP5N:A:unc", "command [options] [data]", long_options, "Provides direct access to the cluster configuration." "\n\n Allows the configuration, or sections of it, to be queried, modified, replaced and deleted." "\n\n Where necessary, XML data will be obtained using -X, -x, or -p options\n"); if(argc < 2) { crm_help('?',LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30; } break; case 'A': obj_type = crm_strdup(optarg); command_options |= cib_xpath; break; case 'u': cib_action = CIB_OP_UPGRADE; dangerous_cmd = TRUE; break; case 'E': cib_action = CIB_OP_ERASE; dangerous_cmd = TRUE; 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 'c': command_options |= cib_can_create; break; case 'n': command_options |= cib_no_children; break; case 'm': cib_action = CIB_OP_ISMASTER; command_options |= cib_scope_local; break; case 'B': cib_action = CIB_OP_BUMP; break; case 'r': dangerous_cmd = TRUE; cib_action = CIB_OP_SLAVE; break; case 'w': dangerous_cmd = TRUE; 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 '?': case '$': crm_help(flag, 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': dangerous_cmd = TRUE; command_options |= cib_inhibit_bcast; command_options |= cib_scope_local; break; case 's': command_options |= cib_sync_call; break; case 'f': force_flag = TRUE; 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"); crm_help('?', LSB_EXIT_EINVAL); } if (optind > argc || cib_action == NULL) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(dangerous_cmd && force_flag == FALSE) { fprintf(stderr, "The supplied command is considered dangerous." " To prevent accidental destruction of the cluster," " the --force flag is required in order to proceed.\n"); fflush(stderr); exit(LSB_EXIT_GENERIC); } if(admin_input_file != NULL) { input = filename2xml(admin_input_file); source = admin_input_file; } else if(admin_input_xml != NULL) { source = "input string"; input = string2xml(admin_input_xml); } else if(admin_input_stdin) { source = "STDIN"; input = stdin2xml(); } if(input != NULL) { crm_log_xml_debug(input, "[admin input]"); } else if(source) { fprintf(stderr, "Couldn't parse input from %s.\n", source); return 1; } 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; the_cib->cmds->register_callback( the_cib, request_id, message_timeout_ms, FALSE, NULL, "cibadmin_op_callback", cibadmin_op_callback); mainloop = g_main_new(FALSE); 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(crm_str_eq(cib_action, CIB_OP_UPGRADE, TRUE)) { if(exit_code == cib_dtd_validation) { xmlNode *obj = NULL; int version = 0, rc = 0; rc = the_cib->cmds->query(the_cib, NULL, &obj, command_options); if(rc == cib_ok) { update_validation(&obj, &version, TRUE, FALSE); } } } } if(output != NULL) { char *buffer = dump_xml_formatted(output); fprintf(stdout, "%s\n", crm_str(buffer)); crm_free(buffer); } the_cib->cmds->signoff(the_cib); crm_debug_3("%s exiting normally", crm_system_name); return -exit_code; } int do_work(xmlNode *input, int call_options, xmlNode **output) { /* construct the request */ the_cib->call_timeout = message_timeout_ms; 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); 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 cib_connection_destroy(gpointer user_data) { crm_err("Connection to the CIB terminated... exiting"); g_main_quit(mainloop); return; } void cibadmin_op_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data) { char *admin_input_xml = NULL; 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_xml(LOG_ERR, "no output", 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/tools/crm_attribute.c b/tools/crm_attribute.c index 40e63554d7..58c5d12ef4 100644 --- a/tools/crm_attribute.c +++ b/tools/crm_attribute.c @@ -1,283 +1,285 @@ /* * 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 gboolean BE_QUIET = FALSE; char command = 'G'; char *dest_uname = NULL; char *dest_node = NULL; char *set_name = NULL; char *attr_id = NULL; char *attr_name = NULL; const char *type = NULL; const char *rsc_id = NULL; const char *attr_value = NULL; const char *attr_default = NULL; static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, - {"quiet", 0, 0, 'Q', "Print only the value on stdout\n"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, + {"quiet", 0, 0, 'Q', "\tPrint only the value on stdout\n"}, - {"attr-name", 1, 0, 'n', "Name of the attribute/option to operate on\n"}, + {"name", 1, 0, 'n', "Name of the attribute/option to operate on"}, {"-spacer-", 0, 0, '-', "\nCommands:"}, - {"query", 0, 0, 'G', "Query the current value of the attribute/option"}, + {"query", 0, 0, 'G', "\tQuery the current value of the attribute/option"}, {"update", 1, 0, 'v', "Update the value of the attribute/option"}, - {"delete", 0, 0, 'D', "Delete the attribute/option"}, + {"delete", 0, 0, 'D', "\tDelete the attribute/option"}, - {"-spacer-", 0, 0, '-', "\nCommand Modifiers:"}, - {"node", 1, 0, 'N', "Set the value for a node other than this one"}, + {"-spacer-", 0, 0, '-', "\nAdditional Options:"}, + {"node", 1, 0, 'N', "Set an attribute for the named node (instead of a cluster option). See also: -l"}, {"type", 1, 0, 't', "Which part of the configuration to update/delete/query the option in."}, - {"-spacer-", 0, 0, '-', "\tValid values: crm_config, rsc_defaults, op_defaults"}, + {"-spacer-", 0, 0, '-', "\t\t\tValid values: crm_config, rsc_defaults, op_defaults"}, {"lifetime", 1, 0, 'l', "Lifetime of the node attribute."}, - {"-spacer-", 0, 0, '-', "\tValid values: reboot, forever"}, + {"-spacer-", 0, 0, '-', "\t\t\tValid values: reboot, forever"}, {"set-name", 1, 0, 's', "(Advanced) The attribute set in which to place the value"}, - {"attr-id", 1, 0, 'i', "(Advanced) The ID used to identify the attribute"}, + {"id", 1, 0, 'i', "\t(Advanced) The ID used to identify the attribute"}, {"default", 1, 0, 'd', "(Advanced) The default value to display if none is found in the configuration"}, {"inhibit-policy-engine", 0, 0, '!', NULL, 1}, /* legacy */ {"node-uname", 1, 0, 'U', NULL, 1}, {"node-uuid", 1, 0, 'u', NULL, 1}, {"get-value", 0, 0, 'G', NULL, 1}, {"delete-attr", 0, 0, 'D', NULL, 1}, {"attr-value", 1, 0, 'v', NULL, 1}, - + {"attr-name", 1, 0, 'n', NULL, 1}, + {"attr-id", 1, 0, 'i', NULL, 1}, + {0, 0, 0, 0} }; int main(int argc, char **argv) { cib_t * the_cib = NULL; enum cib_errors rc = cib_ok; int cib_opts = cib_sync_call; int argerr = 0; int flag; int option_index = 0; crm_log_init(basename(argv[0]), LOG_ERR, FALSE, FALSE, argc, argv); - crm_set_options("V?$GDQN:U:u:s:n:v:l:t:i:!r:d:", "{command} -n {attribute} [-t|-l] [options]", long_options, "Manage node's attributes and cluster options." + crm_set_options("V?$GDQN:U:u:s:n:v:l:t:i:!r:d:", "command -n attribute [options]", long_options, "Manage node's attributes and cluster options." "\n Allows node attributes and cluster options to be queried, modified and deleted."); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'D': case 'G': case 'v': command = flag; attr_value = optarg; break; case 'Q': BE_QUIET = TRUE; break; case 'U': case 'N': dest_uname = crm_strdup(optarg); break; case 'u': dest_node = crm_strdup(optarg); break; case 's': set_name = crm_strdup(optarg); break; case 'l': case 't': type = optarg; break; case 'n': attr_name = crm_strdup(optarg); break; case 'i': attr_id = crm_strdup(optarg); break; case 'r': rsc_id = optarg; break; case 'd': attr_default = optarg; break; case '!': crm_warn("Inhibiting notifications for this update"); cib_opts |= cib_inhibit_notify; 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) { crm_help('?', LSB_EXIT_GENERIC); } the_cib = cib_new(); rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); if(rc != cib_ok) { fprintf(stderr, "Error signing on to the CIB service: %s\n", cib_error2string(rc)); return rc; } if(safe_str_eq(type, "reboot")) { type = XML_CIB_TAG_STATUS; } else if(safe_str_eq(type, "forever")) { type = XML_CIB_TAG_NODES; } if(type == NULL && dest_uname == NULL) { /* we're updating cluster options - dont populate dest_node */ type = XML_CIB_TAG_CRMCONFIG; } else { determine_host(the_cib, &dest_uname, &dest_node); } if(rc != cib_ok) { crm_info("Error during setup of %s=%s update", attr_name, command=='D'?"":attr_value); } else if( (command=='v' || command=='D') && safe_str_eq(type, XML_CIB_TAG_STATUS) && attrd_lazy_update(command, dest_uname, attr_name, attr_value, type, set_name, NULL)) { crm_info("Update %s=%s sent via attrd", attr_name, command=='D'?"":attr_value); } else if(command=='D') { rc = delete_attr(the_cib, cib_opts, type, dest_node, set_name, attr_id, attr_name, attr_value, TRUE); if(rc == cib_NOTEXISTS) { /* Nothing to delete... * which means its not there... * which is what the admin wanted */ rc = cib_ok; } else if(rc != cib_missing_data && safe_str_eq(crm_system_name, "crm_failcount")) { char *now_s = NULL; time_t now = time(NULL); now_s = crm_itoa(now); update_attr(the_cib, cib_sync_call, XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, "last-lrm-refresh", now_s, TRUE); crm_free(now_s); } } else if(command=='v') { CRM_DEV_ASSERT(type != NULL); CRM_DEV_ASSERT(attr_name != NULL); CRM_DEV_ASSERT(attr_value != NULL); rc = update_attr(the_cib, cib_opts, type, dest_node, set_name, attr_id, attr_name, attr_value, TRUE); } else /* query */ { char *read_value = NULL; rc = read_attr(the_cib, type, dest_node, set_name, attr_id, attr_name, &read_value, TRUE); if(rc == cib_NOTEXISTS && attr_default) { read_value = crm_strdup(attr_default); rc = cib_ok; } crm_info("Read %s=%s %s%s", attr_name, crm_str(read_value), set_name?"in ":"", set_name?set_name:""); if(rc == cib_missing_data) { rc = cib_ok; } else if(BE_QUIET == FALSE) { fprintf(stdout, "%s%s %s%s %s%s value=%s\n", type?"scope=":"", type?type:"", attr_id?"id=":"", attr_id?attr_id:"", attr_name?"name=":"", attr_name?attr_name:"", read_value?read_value:"(null)"); } else if(read_value != NULL) { fprintf(stdout, "%s\n", read_value); } } the_cib->cmds->signoff(the_cib); if(rc == cib_missing_data) { printf("Please choose from one of the matches above and suppy the 'id' with --attr-id\n"); } else if(rc != cib_ok) { fprintf(stderr, "Error performing operation: %s\n", cib_error2string(rc)); } return rc; } diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 85ffb7928f..3f39968d87 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1,1657 +1,1659 @@ /* * 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 <../lib/pengine/unpack.h> /* GMainLoop *mainloop = NULL; */ void wait_for_refresh(int offset, const char *prefix, int msec); void clean_up(int rc); void crm_diff_update(const char *event, xmlNode *msg); gboolean mon_refresh_display(gpointer user_data); int cib_connect(gboolean full); char *xml_file = NULL; char *as_html_file = NULL; char *pid_file = NULL; char *snmp_target = NULL; gboolean as_console = TRUE;; gboolean simple_status = FALSE; gboolean group_by_node = FALSE; gboolean inactive_resources = FALSE; gboolean web_cgi = FALSE; int reconnect_msec = 5000; gboolean daemonize = FALSE; GMainLoop *mainloop = NULL; guint timer_id = 0; const char *crm_mail_host = NULL; const char *crm_mail_prefix = NULL; const char *crm_mail_from = NULL; const char *crm_mail_to = NULL; cib_t *cib = NULL; xmlNode *current_cib = NULL; gboolean one_shot = FALSE; gboolean has_warnings = FALSE; gboolean print_failcount = FALSE; gboolean print_operations = FALSE; gboolean print_timing = FALSE; gboolean log_diffs = FALSE; gboolean log_updates = FALSE; long last_refresh = 0; crm_trigger_t *refresh_trigger = NULL; /* * 1.3.6.1.4.1.32723 has been assigned to the project by IANA * http://www.iana.org/assignments/enterprise-numbers */ #define PACEMAKER_PREFIX "1.3.6.1.4.1.32723" #define PACEMAKER_TRAP_PREFIX PACEMAKER_PREFIX ".1" #define snmp_crm_trap_oid PACEMAKER_TRAP_PREFIX #define snmp_crm_oid_node PACEMAKER_TRAP_PREFIX ".1" #define snmp_crm_oid_rsc PACEMAKER_TRAP_PREFIX ".2" #define snmp_crm_oid_task PACEMAKER_TRAP_PREFIX ".3" #define snmp_crm_oid_desc PACEMAKER_TRAP_PREFIX ".4" #define snmp_crm_oid_status PACEMAKER_TRAP_PREFIX ".5" #define snmp_crm_oid_rc PACEMAKER_TRAP_PREFIX ".6" #define snmp_crm_oid_trc PACEMAKER_TRAP_PREFIX ".7" #if CURSES_ENABLED # define print_dot() if(as_console) { \ printw("."); \ clrtoeol(); \ refresh(); \ } else { \ fprintf(stdout, "."); \ } #else # define print_dot() fprintf(stdout, "."); #endif #if CURSES_ENABLED # define print_as(fmt, args...) if(as_console) { \ printw(fmt, ##args); \ clrtoeol(); \ refresh(); \ } else { \ fprintf(stdout, fmt, ##args); \ } #else # define print_as(fmt, args...) fprintf(stdout, fmt, ##args); #endif static void blank_screen(void) { #if CURSES_ENABLED int lpc = 0; for(lpc = 0; lpc < LINES; lpc++) { move(lpc, 0); clrtoeol(); } move(0, 0); refresh(); #endif } static gboolean mon_timer_popped(gpointer data) { int rc = cib_ok; if(timer_id > 0) { g_source_remove(timer_id); } rc = cib_connect(TRUE); if(rc != cib_ok) { print_dot(); timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL); } return FALSE; } static void mon_cib_connection_destroy(gpointer user_data) { print_as("Connection to the CIB terminated\n"); if(cib) { print_as("Reconnecting..."); cib->cmds->signoff(cib); timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL); } return; } /* * Mainloop signal handler. */ static void mon_shutdown(int nsig) { clean_up(LSB_EXIT_OK); } int cib_connect(gboolean full) { int rc = cib_ok; static gboolean need_pass = TRUE; CRM_CHECK(cib != NULL, return cib_missing); if(cib->state != cib_connected_query && cib->state != cib_connected_command) { crm_debug_4("Connecting to the CIB"); if(need_pass && cib->variant == cib_remote) { need_pass = FALSE; print_as("Password:"); } rc = cib->cmds->signon(cib, crm_system_name, cib_query); if(rc != cib_ok) { return rc; } current_cib = get_cib_copy(cib); mon_refresh_display(NULL); if(full) { if(rc == cib_ok) { rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy); } if(rc == cib_ok) { cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update); rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update); } if(rc != cib_ok) { print_as("Notification setup failed, could not monitor CIB actions"); if(as_console) { sleep(2); } clean_up(-rc); } } } return rc; } static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, - - {"interval", 1, 0, 'i', "Update frequency in seconds" }, - {"one-shot", 0, 0, '1', "\tDisplay the cluster status once on the console and exit (doesnt use ncurses)"}, - {"disable-ncurses",0, 0, 'N', "Disable the use of ncurses"}, - {"daemonize", 0, 0, 'd', "\tRun in the background as a daemon"}, - {"pid-file", 1, 0, 'p', "Daemon pid file location\n"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, + {"-spacer-", 1, 0, '-', "\nModes:"}, + {"as-html", 1, 0, 'h', "Write cluster status to the named file"}, + {"web-cgi", 0, 0, 'w', "\tWeb mode with output suitable for cgi"}, + {"simple-status", 0, 0, 's', "Display the cluster status once as a simple one line output (suitable for nagios)"}, + {"snmp-traps", 0, 0, 'S', "Send SNMP traps to this station"}, + {"mail-to", 1, 0, 'T', "Send Mail alerts to this user. See also --mail-from, --mail-host, --mail-prefix"}, + + {"-spacer-", 1, 0, '-', "\nDisplay Options:"}, {"group-by-node", 0, 0, 'n', "Group resources by node" }, - {"inactive", 0, 0, 'r', "\tDisplay inactive resources" }, + {"inactive", 0, 0, 'r', "Display inactive resources" }, {"failcounts", 0, 0, 'f', "Display resource fail counts"}, {"operations", 0, 0, 'o', "Display resource operation history" }, {"timing-details", 0, 0, 't', "Display resource operation history with timing details\n" }, - {"snmp-traps", 0, 0, 'S', "Send SNMP traps to this station\n"}, + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {"interval", 1, 0, 'i', "\tUpdate frequency in seconds" }, + {"one-shot", 0, 0, '1', "\tDisplay the cluster status once on the console and exit (doesnt use ncurses)"}, + {"disable-ncurses",0, 0, 'N', "\tDisable the use of ncurses"}, + {"daemonize", 0, 0, 'd', "\tRun in the background as a daemon"}, + {"pid-file", 1, 0, 'p', "\t(Advanced) Daemon pid file location"}, + {"mail-from", 1, 0, 'F', "\tMail alerts should come from the named user"}, + {"mail-host", 1, 0, 'H', "\tMail alerts should be sent via the named host"}, + {"mail-prefix", 1, 0, 'P', "Subjects for mail alerts should start with this string"}, - {"as-html", 1, 0, 'h', "Write cluster status to the named file"}, - {"web-cgi", 0, 0, 'w', "\tWeb mode with output suitable for cgi"}, - {"simple-status", 0, 0, 's', "Display the cluster status once as a simple one line output (suitable for nagios)\n"}, - - {"mail-to", 1, 0, 'T'}, - {"mail-from", 1, 0, 'F'}, - {"mail-host", 1, 0, 'H'}, - {"mail-prefix", 1, 0, 'P'}, {"xml-file", 1, 0, 'x', NULL, 1}, {NULL, 0, 0, 0} }; int main(int argc, char **argv) { int flag; int argerr = 0; int exit_code = 0; int option_index = 0; pid_file = crm_strdup("/tmp/ClusterMon.pid"); crm_log_init(basename(argv[0]), LOG_CRIT, FALSE, FALSE, 0, NULL); - crm_set_options("V?$i:nrh:dp:s1wx:oftNS:T:F:H:P:", "[-?|-v|-1|-N|-d -p] [-h|-w|-s] [-S] [-T -F -H -P] [-i] [-n] [-r] [-f] [-o|-t]", long_options, + crm_set_options("V?$i:nrh:dp:s1wx:oftNS:T:F:H:P:", "mode [options]", long_options, "Provides a summary of cluster's current state.\n Outputs varying levels of detail in a number of different formats.\n"); if (strcmp(crm_system_name, "crm_mon.cgi")==0) { web_cgi = TRUE; one_shot = TRUE; } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case 'i': reconnect_msec = crm_get_msec(optarg); break; case 'n': group_by_node = TRUE; break; case 'r': inactive_resources = TRUE; break; case 'd': daemonize = TRUE; break; case 't': print_timing = TRUE; print_operations = TRUE; break; case 'o': print_operations = TRUE; break; case 'f': print_failcount = TRUE; break; case 'p': crm_free(pid_file); pid_file = crm_strdup(optarg); break; case 'x': xml_file = crm_strdup(optarg); one_shot = TRUE; break; case 'h': as_html_file = crm_strdup(optarg); break; case 'w': web_cgi = TRUE; one_shot = TRUE; break; case 's': simple_status = TRUE; one_shot = TRUE; break; case 'S': snmp_target = optarg; break; case 'T': crm_mail_to = optarg; break; case 'F': crm_mail_from = optarg; break; case 'H': crm_mail_host = optarg; break; case 'P': crm_mail_prefix = optarg; break; case '1': one_shot = TRUE; break; case 'N': as_console = FALSE; break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); 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 (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(one_shot) { as_console = FALSE; } else if(daemonize) { as_console = FALSE; cl_log_enable_stderr(FALSE); if(!as_html_file && !snmp_target && !crm_mail_to) { printf("Looks like you forgot to specify one or more of: --as-html, --mail-to, --snmp-target\n"); crm_help('?', LSB_EXIT_GENERIC); } crm_make_daemon(crm_system_name, TRUE, pid_file); } else if(as_console) { #if CURSES_ENABLED initscr(); cbreak(); noecho(); cl_log_enable_stderr(FALSE); #else one_shot = TRUE; as_console = FALSE; printf("Defaulting to one-shot mode\n"); printf("You need to have curses available at compile time to enable console mode\n"); #endif } crm_info("Starting %s", crm_system_name); if(xml_file != NULL) { current_cib = filename2xml(xml_file); mon_refresh_display(NULL); return exit_code; } if(current_cib == NULL) { cib = cib_new(); if(!one_shot) { print_as("Attempting connection to the cluster..."); } do { exit_code = cib_connect(!one_shot); if(one_shot) { break; } else if(exit_code != cib_ok) { print_dot(); sleep(reconnect_msec/1000); } } while(exit_code == cib_connection); if(exit_code != cib_ok) { print_as("\nConnection to cluster failed: %s\n", cib_error2string(exit_code)); if(as_console) { sleep(2); } clean_up(-exit_code); } } if(one_shot) { return exit_code; } mainloop = g_main_new(FALSE); mainloop_add_signal(SIGTERM, mon_shutdown); mainloop_add_signal(SIGINT, mon_shutdown); refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL); g_main_run(mainloop); g_main_destroy(mainloop); crm_info("Exiting %s", crm_system_name); clean_up(0); return 0; /* never reached */ } void wait_for_refresh(int offset, const char *prefix, int msec) { int lpc = msec / 1000; struct timespec sleept = {1 , 0}; if(as_console == FALSE) { timer_id = g_timeout_add(msec, mon_timer_popped, NULL); return; } crm_notice("%sRefresh in %ds...", prefix?prefix:"", lpc); while(lpc > 0) { #if CURSES_ENABLED move(offset, 0); /* printw("%sRefresh in \033[01;32m%ds\033[00m...", prefix?prefix:"", lpc); */ printw("%sRefresh in %ds...\n", prefix?prefix:"", lpc); clrtoeol(); refresh(); #endif lpc--; if(lpc == 0) { timer_id = g_timeout_add( 1000, mon_timer_popped, NULL); } else { if (nanosleep(&sleept, NULL) != 0) { return; } } } } #define mon_warn(fmt...) do { \ if (!has_warnings) { \ print_as("Warning:"); \ } else { \ print_as(","); \ } \ print_as(fmt); \ has_warnings = TRUE; \ } while(0) static int print_simple_status(pe_working_set_t *data_set) { node_t *dc = NULL; int nodes_online = 0; int nodes_standby = 0; dc = data_set->dc_node; if(dc == NULL) { mon_warn("No DC "); } slist_iter(node, node_t, data_set->nodes, lpc2, if(node->details->standby && node->details->online) { nodes_standby++; } else if(node->details->online) { nodes_online++; } else { mon_warn("offline node: %s", node->details->uname); } ); if (!has_warnings) { print_as("Ok: %d nodes online", nodes_online); if (nodes_standby > 0) { print_as(", %d standby nodes", nodes_standby); } print_as(", %d resources configured", g_list_length(data_set->resources)); } print_as("\n"); return 0; } extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set); static void get_ping_score(node_t *node, pe_working_set_t *data_set) { const char *attr = "pingd"; const char *value = NULL; value = g_hash_table_lookup(node->details->attrs, attr); if(value != NULL) { print_as(" %s=%s", attr, value); } } 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; } } print_as("'%s'", date_str); } static void print_rsc_summary(pe_working_set_t *data_set, node_t *node, resource_t *rsc, gboolean all) { gboolean printed = FALSE; time_t last_failure = 0; int failcount = get_failcount(node, rsc, (int*)&last_failure, data_set); if(all || failcount || last_failure > 0) { printed = TRUE; print_as(" %s: migration-threshold=%d", rsc->id, rsc->migration_threshold); } if(failcount > 0) { printed = TRUE; print_as(" fail-count=%d", failcount); } if(last_failure > 0) { printed = TRUE; print_as(" last-failure="); print_date(last_failure); } if(printed) { print_as("\n"); } } static void print_rsc_history(pe_working_set_t *data_set, node_t *node, xmlNode *rsc_entry) { GListPtr op_list = NULL; gboolean print_name = TRUE; GListPtr sorted_op_list = NULL; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); xml_child_iter_filter( rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, op_list = g_list_append(op_list, rsc_op); ); sorted_op_list = g_list_sort(op_list, sort_op_by_callid); slist_iter(xml_op, xmlNode, sorted_op_list, lpc, const char *value = NULL; const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); const char *interval = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); int rc = crm_parse_int(op_rc, "0"); if(safe_str_eq(task, CRMD_ACTION_STATUS) && safe_str_eq(interval, "0")) { task = "probe"; } if(rc == 7 && safe_str_eq(task, "probe")) { continue; } else if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) { continue; } if(print_name) { print_name = FALSE; if(rsc == NULL) { print_as("Orphan resource: %s", rsc_id); } else { print_rsc_summary(data_set, node, rsc, TRUE); } } print_as(" + (%s) %s:", call, task); if(safe_str_neq(interval, "0")) { print_as(" interval=%sms", interval); } if(print_timing) { int int_value; const char *attr = "last-rc-change"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=", attr); print_date(int_value); } attr = "last-run"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=", attr); print_date(int_value); } attr = "exec-time"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=%dms", attr, int_value); } attr = "queue-time"; value = crm_element_value(xml_op, attr); if(value) { int_value = crm_parse_int(value, NULL); print_as(" %s=%dms", attr, int_value); } } print_as(" rc=%s (%s)\n", op_rc, execra_code2string(rc)); ); /* no need to free the contents */ g_list_free(sorted_op_list); } static void print_node_summary(pe_working_set_t *data_set, gboolean operations) { xmlNode *lrm_rsc = NULL; xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); if(operations) { print_as("\nOperations:\n"); } else { print_as("\nMigration summary:\n"); } xml_child_iter_filter( cib_status, node_state, XML_CIB_TAG_STATE, node_t *node = pe_find_node_id(data_set->nodes, ID(node_state)); if(node == NULL || node->details->online == FALSE){ continue; } print_as("* Node %s: ", crm_element_value(node_state, XML_ATTR_UNAME)); get_ping_score(node, data_set); print_as("\n"); lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); xml_child_iter_filter( lrm_rsc, rsc_entry, XML_LRM_TAG_RESOURCE, if(operations) { print_rsc_history(data_set, node, rsc_entry); } else { const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); if(rsc) { print_rsc_summary(data_set, node, rsc, FALSE); } else { print_as(" %s: orphan\n", rsc_id); } } ); ); } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static int print_status(pe_working_set_t *data_set) { static int updates = 0; node_t *dc = NULL; char *since_epoch = NULL; char *online_nodes = NULL; char *offline_nodes = NULL; xmlNode *dc_version = NULL; xmlNode *quorum_node = NULL; time_t a_time = time(NULL); int configured_resources = 0; int print_opts = pe_print_ncurses; const char *quorum_votes = "unknown"; if(as_console) { blank_screen(); } else { print_opts = pe_print_printf; } updates++; dc = data_set->dc_node; print_as("\n\n============\n"); if(a_time == (time_t)-1) { crm_perror(LOG_ERR,"set_node_tstamp(): Invalid time returned"); return 1; } since_epoch = ctime(&a_time); if(since_epoch != NULL) { print_as("Last updated: %s", since_epoch); } dc_version = get_xpath_object("//nvpair[@name='dc-version']", data_set->input, LOG_DEBUG); if(dc == NULL) { print_as("Current DC: NONE\n"); } else { const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); if(safe_str_neq(dc->details->uname, dc->details->id)) { print_as("Current DC: %s (%s)", dc->details->uname, dc->details->id); } else { print_as("Current DC: %s", dc->details->uname); } print_as(" - partition %s quorum\n", crm_is_true(quorum)?"with":"WITHOUT"); if(dc_version) { print_as("Version: %s\n", crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)); } } quorum_node = get_xpath_object("//nvpair[@name='expected-quorum-votes']", data_set->input, LOG_DEBUG); if(quorum_node) { quorum_votes = crm_element_value(quorum_node, XML_NVPAIR_ATTR_VALUE); } slist_iter(rsc, resource_t, data_set->resources, lpc, if(is_not_set(rsc->flags, pe_rsc_orphan)) { configured_resources++; } ); print_as("%d Nodes configured, %s expected votes\n", g_list_length(data_set->nodes), quorum_votes); print_as("%d Resources configured.\n", configured_resources); print_as("============\n\n"); slist_iter(node, node_t, data_set->nodes, lpc2, const char *node_mode = NULL; if(node->details->unclean) { if(node->details->online && node->details->unclean) { node_mode = "UNCLEAN (online)"; } else if(node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if(node->details->pending) { node_mode = "pending"; } else if(node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if(node->details->standby) { if(node->details->online) { node_mode = "standby"; } else { node_mode = "OFFLINE (standby)"; } } else if(node->details->online) { node_mode = "online"; if(group_by_node == FALSE) { online_nodes = add_list_element(online_nodes, node->details->uname); continue; } } else { node_mode = "OFFLINE"; if(group_by_node == FALSE) { offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } } if(safe_str_eq(node->details->uname, node->details->id)) { print_as("Node %s: %s\n", node->details->uname, node_mode); } else { print_as("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } if(group_by_node) { slist_iter(rsc, resource_t, node->details->running_rsc, lpc2, rsc->fns->print( rsc, "\t", print_opts|pe_print_rsconly, stdout); ); } ); if(online_nodes) { print_as("Online: [%s ]\n", online_nodes); crm_free(online_nodes); } if(offline_nodes) { print_as("OFFLINE: [%s ]\n", offline_nodes); crm_free(offline_nodes); } if(group_by_node == FALSE && inactive_resources) { print_as("\nFull list of resources:\n"); } else if(inactive_resources) { print_as("\nInactive resources:\n"); } if(group_by_node == FALSE || inactive_resources) { print_as("\n"); slist_iter(rsc, resource_t, data_set->resources, lpc2, gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); if(is_set(rsc->flags, pe_rsc_orphan) && is_active == FALSE) { continue; } else if(group_by_node == FALSE) { if(partially_active || inactive_resources) { rsc->fns->print(rsc, NULL, print_opts, stdout); } } else if(is_active == FALSE && inactive_resources) { rsc->fns->print(rsc, NULL, print_opts, stdout); } ); } if(print_operations || print_failcount) { print_node_summary(data_set, print_operations); } if(xml_has_children(data_set->failed)) { print_as("\nFailed actions:\n"); xml_child_iter(data_set->failed, xml_op, int val = 0; const char *id = ID(xml_op); const char *last = crm_element_value(xml_op, "last_run"); const char *node = crm_element_value(xml_op, XML_ATTR_UNAME); const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); const char *rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); const char *status = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); val = crm_parse_int(status, "0"); print_as(" %s (node=%s, call=%s, rc=%s, status=%s", id, node, call, rc, op_status2text(val)); if(last) { time_t run_at = crm_parse_int(last, "0"); print_as(", last-run=%s, queued=%sms, exec=%sms\n", ctime(&run_at), crm_element_value(xml_op, "exec_time"), crm_element_value(xml_op, "queue_time")); } val = crm_parse_int(rc, "0"); print_as("): %s\n", execra_code2string(val)); ); } #if CURSES_ENABLED if(as_console) { refresh(); } #endif return 0; } static int print_html_status(pe_working_set_t *data_set, const char *filename, gboolean web_cgi) { FILE *stream; node_t *dc = NULL; static int updates = 0; char *filename_tmp = NULL; if (web_cgi) { stream=stdout; fprintf(stream, "Content-type: text/html\n\n"); } else { filename_tmp = crm_concat(filename, "tmp", '.'); stream = fopen(filename_tmp, "w"); if(stream == NULL) { crm_perror(LOG_ERR,"Cannot open %s for writing", filename_tmp); crm_free(filename_tmp); return -1; } } updates++; dc = data_set->dc_node; fprintf(stream, ""); fprintf(stream, ""); fprintf(stream, "Cluster status"); /* content="%d;url=http://webdesign.about.com" */ fprintf(stream, "", reconnect_msec); fprintf(stream, ""); /*** SUMMARY ***/ fprintf(stream, "

Cluster summary

"); { char *now_str = NULL; time_t now = time(NULL); now_str = ctime(&now); now_str[24] = EOS; /* replace the newline */ fprintf(stream, "Last updated: %s
\n", now_str); } if(dc == NULL) { fprintf(stream, "Current DC: NONE
"); } else { fprintf(stream, "Current DC: %s (%s)
", dc->details->uname, dc->details->id); } fprintf(stream, "%d Nodes configured.
", g_list_length(data_set->nodes)); fprintf(stream, "%d Resources configured.
", g_list_length(data_set->resources)); /*** CONFIG ***/ fprintf(stream, "

Config Options

\n"); fprintf(stream, "\n"); fprintf(stream, "\n", data_set->default_resource_stickiness); fprintf(stream, "flags, pe_flag_stonith_enabled)?"enabled":"disabled"); fprintf(stream, "\n", is_set(data_set->flags, pe_flag_symmetric_cluster)?"":"a-"); fprintf(stream, "\n
Default resource stickiness:%d
STONITH of failed nodes:%s
Cluster is:%ssymmetric
No Quorum Policy:"); switch (data_set->no_quorum_policy) { case no_quorum_freeze: fprintf(stream, "Freeze resources"); break; case no_quorum_stop: fprintf(stream, "Stop ALL resources"); break; case no_quorum_ignore: fprintf(stream, "Ignore"); break; case no_quorum_suicide: fprintf(stream, "Suicide"); break; } fprintf(stream, "\n
\n"); /*** NODE LIST ***/ fprintf(stream, "

Node List

\n"); fprintf(stream, "
    \n"); slist_iter(node, node_t, data_set->nodes, lpc2, fprintf(stream, "
  • "); if(node->details->standby_onfail && node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"standby (on-fail)\n"); } else if(node->details->standby && node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"standby\n"); } else if(node->details->standby) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"OFFLINE (standby)\n"); } else if(node->details->online) { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"online\n"); } else { fprintf(stream, "Node: %s (%s): %s",node->details->uname, node->details->id,"OFFLINE\n"); } if(group_by_node) { fprintf(stream, "
      \n"); slist_iter(rsc, resource_t, node->details->running_rsc, lpc2, fprintf(stream, "
    • "); rsc->fns->print(rsc, NULL, pe_print_html|pe_print_rsconly, stream); fprintf(stream, "
    • \n"); ); fprintf(stream, "
    \n"); } fprintf(stream, "
  • \n"); ); fprintf(stream, "
\n"); if(group_by_node && inactive_resources) { fprintf(stream, "

(Partially) Inactive Resources

\n"); } else if(group_by_node == FALSE) { fprintf(stream, "

Resource List

\n"); } if(group_by_node == FALSE || inactive_resources) { slist_iter(rsc, resource_t, data_set->resources, lpc2, if(group_by_node && rsc->fns->active(rsc, TRUE)) { continue; } rsc->fns->print(rsc, NULL, pe_print_html, stream); ); } fprintf(stream, ""); fflush(stream); fclose(stream); if (!web_cgi) { if(rename(filename_tmp, filename) != 0) { crm_perror(LOG_ERR,"Unable to rename %s->%s", filename_tmp, filename); } crm_free(filename_tmp); } return 0; } #if ENABLE_SNMP #include #include #include #include #include #include #define add_snmp_field(list, oid_string, value) do { \ oid name[MAX_OID_LEN]; \ size_t name_length = MAX_OID_LEN; \ if (snmp_parse_oid(oid_string, name, &name_length)) { \ int s_rc = snmp_add_var(list, name, name_length, 's', (value)); \ if(s_rc != 0) { \ crm_err("Could not add %s=%s rc=%d", oid_string, value, s_rc); \ } else { \ crm_debug_2("Added %s=%s", oid_string, value); \ } \ } else { \ crm_err("Could not parse OID: %s", oid_string); \ } \ } while(0) \ #define add_snmp_field_int(list, oid_string, value) do { \ oid name[MAX_OID_LEN]; \ size_t name_length = MAX_OID_LEN; \ if (snmp_parse_oid(oid_string, name, &name_length)) { \ if(NULL == snmp_pdu_add_variable( \ list, name, name_length, ASN_INTEGER, \ (u_char *) & value, sizeof(value))) { \ crm_err("Could not add %s=%d", oid_string, value); \ } else { \ crm_debug_2("Added %s=%d", oid_string, value); \ } \ } else { \ crm_err("Could not parse OID: %s", oid_string); \ } \ } while(0) \ static int snmp_input(int operation, netsnmp_session *session, int reqid, netsnmp_pdu *pdu, void *magic) { return 1; } static netsnmp_session *crm_snmp_init(const char *target) { static netsnmp_session *session = NULL; if(session) { return session; } if(target == NULL) { return NULL; } if(crm_log_level > LOG_INFO) { char *debug_tokens = crm_strdup("run:shell,snmptrap,tdomain"); debug_register_tokens(debug_tokens); snmp_set_do_debugging(1); } crm_malloc0(session, sizeof(netsnmp_session)); snmp_sess_init(session); session->version = SNMP_VERSION_2c; session->callback = snmp_input; session->callback_magic = NULL; session = snmp_add(session, netsnmp_transport_open_client("snmptrap", target), NULL, NULL); if (session == NULL) { snmp_sess_perror("Could not create snmp transport", session); } return session; } #endif static int send_snmp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { int ret = 1; #if ENABLE_SNMP static oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 }; static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 }; netsnmp_pdu *trap_pdu; netsnmp_session *session = crm_snmp_init(snmp_target); trap_pdu = snmp_pdu_create(SNMP_MSG_TRAP2); if ( !trap_pdu ) { crm_err("Failed to create SNMP notification"); return SNMPERR_GENERR; } if(1) { /* send uptime */ char csysuptime[20]; time_t now = time(NULL); sprintf(csysuptime, "%ld", now); snmp_add_var(trap_pdu, sysuptime_oid, sizeof(sysuptime_oid) / sizeof(oid), 't', csysuptime); } /* Indicate what the trap is by setting snmpTrapOid.0 */ ret = snmp_add_var(trap_pdu, snmptrap_oid, sizeof(snmptrap_oid) / sizeof(oid), 'o', snmp_crm_trap_oid); if (ret != 0) { crm_err("Failed set snmpTrapOid.0=%s", snmp_crm_trap_oid); return ret; } /* Add extries to the trap */ add_snmp_field(trap_pdu, snmp_crm_oid_rsc, rsc); add_snmp_field(trap_pdu, snmp_crm_oid_node, node); add_snmp_field(trap_pdu, snmp_crm_oid_task, task); add_snmp_field(trap_pdu, snmp_crm_oid_desc, desc); add_snmp_field_int(trap_pdu, snmp_crm_oid_rc, rc); add_snmp_field_int(trap_pdu, snmp_crm_oid_trc, target_rc); add_snmp_field_int(trap_pdu, snmp_crm_oid_status, status); /* Send and cleanup */ ret = snmp_send(session, trap_pdu); if(ret == 0) { /* error */ snmp_sess_perror("Could not send SNMP trap", session); snmp_free_pdu(trap_pdu); ret = SNMPERR_GENERR; } else { ret = SNMPERR_SUCCESS; } #else crm_err("Sending SNMP traps is not supported by this installation"); #endif return ret; } #if ENABLE_ESMTP #include #include static void print_recipient_status( smtp_recipient_t recipient, const char *mailbox, void *arg) { const smtp_status_t *status; status = smtp_recipient_status (recipient); printf ("%s: %d %s", mailbox, status->code, status->text); } static void event_cb (smtp_session_t session, int event_no, void *arg, ...) { int *ok; va_list alist; va_start(alist, arg); switch(event_no) { case SMTP_EV_CONNECT: case SMTP_EV_MAILSTATUS: case SMTP_EV_RCPTSTATUS: case SMTP_EV_MESSAGEDATA: case SMTP_EV_MESSAGESENT: case SMTP_EV_DISCONNECT: break; case SMTP_EV_WEAK_CIPHER: { int bits = va_arg(alist, long); ok = va_arg(alist, int*); crm_debug("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.", bits); *ok = 1; break; } case SMTP_EV_STARTTLS_OK: crm_debug("SMTP_EV_STARTTLS_OK - TLS started here."); break; case SMTP_EV_INVALID_PEER_CERTIFICATE: { long vfy_result = va_arg(alist, long); ok = va_arg(alist, int*); /* There is a table in handle_invalid_peer_certificate() of mail-file.c */ crm_err("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld", vfy_result); *ok = 1; break; } case SMTP_EV_NO_PEER_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_NO_PEER_CERTIFICATE - accepted."); *ok = 1; break; case SMTP_EV_WRONG_PEER_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."); *ok = 1; break; case SMTP_EV_NO_CLIENT_CERTIFICATE: ok = va_arg(alist, int*); crm_debug("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."); *ok = 1; break; default: crm_debug("Got event: %d - ignored.\n", event_no); } va_end(alist); } #endif #define BODY_MAX 2048 static int send_smtp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc, int status, const char *desc) { #if ENABLE_ESMTP smtp_session_t session; smtp_message_t message; auth_context_t authctx; struct sigaction sa; int len = 10; int noauth = 1; char crm_mail_body[BODY_MAX]; char *crm_mail_subject = NULL; if(node == NULL) { node = "-"; } if(rsc) { rsc = "-"; } if(desc == NULL) { desc = "-"; } if(crm_mail_to == NULL) { return 1; } if(crm_mail_host == NULL) { crm_mail_host = "localhost:25"; } if(crm_mail_prefix == NULL) { crm_mail_prefix = "Cluster notification"; } crm_debug("Sending '%s' mail to %s via %s", crm_mail_prefix, crm_mail_to, crm_mail_host); len += strlen(crm_mail_prefix); len += strlen(node); len += strlen(rsc); len += strlen(desc); crm_malloc0(crm_mail_subject, len); snprintf(crm_mail_subject, len, "%s: %s event for %s on %s: %s", crm_mail_prefix, task, rsc, node, desc); len = 0; len += snprintf(crm_mail_body, BODY_MAX-len, "%s\n", crm_mail_prefix); len += snprintf(crm_mail_body, BODY_MAX-len, "====\n\n"); if(rc==target_rc) { len += snprintf(crm_mail_body, BODY_MAX-len, "Completed operation %s for resource %s on %s\n", task, rsc, node); } else { len += snprintf(crm_mail_body, BODY_MAX-len, "Operation %s for resource %s on %s failed: %s\n", task, rsc, node, desc); } len += snprintf(crm_mail_body, BODY_MAX-len, "\nDetails:\n"); len += snprintf(crm_mail_body, BODY_MAX-len, "\toperation status: (%d) %s\n", status, op_status2text(status)); if(status == LRM_OP_DONE) { len += snprintf(crm_mail_body, BODY_MAX-len, "\tscript returned: (%d) %s\n", rc, execra_code2string(rc)); len += snprintf(crm_mail_body, BODY_MAX-len, "\texpected return value: (%d) %s\n", target_rc, execra_code2string(target_rc)); } auth_client_init(); session = smtp_create_session(); message = smtp_add_message(session); smtp_starttls_enable (session, Starttls_ENABLED); sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGPIPE, &sa, NULL); smtp_set_server (session, crm_mail_host); authctx = auth_create_context (); auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0); smtp_set_eventcb(session, event_cb, NULL); /* Now tell libESMTP it can use the SMTP AUTH extension. */ if (!noauth) { smtp_auth_set_context (session, authctx); } /* NULL is ok */ smtp_set_reverse_path (message, crm_mail_from); #if 1 /* In thoery, the last arg can be NULL if we then call smtp_add_recipient() */ smtp_set_header (message, "To", NULL/*phrase*/, crm_mail_to); /* "Phrase" */ #else smtp_add_recipient (message, crm_mail_to); #endif /* Set the Subject: header and override any subject line in the message headers. */ smtp_set_header (message, "Subject", crm_mail_subject); smtp_set_header_option (message, "Subject", Hdr_OVERRIDE, 1); smtp_set_message_str(message, crm_mail_body); if (!smtp_start_session (session)) { char buf[128]; crm_err("SMTP server problem: %s\n", smtp_strerror (smtp_errno (), buf, sizeof buf)); } else { const smtp_status_t *smtp_status = smtp_message_transfer_status(message); crm_info("Send status: %d %s", smtp_status->code, crm_str(smtp_status->text)); smtp_enumerate_recipients (message, print_recipient_status, NULL); } smtp_destroy_session(session); auth_destroy_context(authctx); auth_client_exit(); #endif return 0; } static void handle_rsc_op(xmlNode *rsc_op) { int rc = -1; int status = -1; int action = -1; int interval = 0; int target_rc = -1; int transition_num = -1; gboolean send_trap = TRUE; gboolean send_email = TRUE; char *rsc = NULL; char *task = NULL; const char *desc = NULL; const char *node = NULL; const char *magic = NULL; const char *id = ID(rsc_op); char *update_te_uuid = NULL; xmlNode *n = rsc_op; magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC); if(magic == NULL) { /* non-change */ return; } if(FALSE == decode_transition_magic( magic, &update_te_uuid, &transition_num, &action, &status, &rc, &target_rc)) { crm_err("Invalid event %s detected for %s", magic, id); return; } if(parse_op_key(id, &rsc, &task, &interval) == FALSE) { crm_err("Invalid event detected for %s", id); return; } while(n != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(n))) { n = n->parent; } node = ID(n); if(node == NULL) { crm_err("No node detected for event %s (%s)", magic, id); return; } /* look up where we expected it to be? */ desc = cib_error2string(cib_ok); if(status == LRM_OP_DONE && target_rc == rc) { crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc); if(rc == EXECRA_NOT_RUNNING) { send_trap = FALSE; } } else if(status == LRM_OP_DONE) { desc = execra_code2string(rc); crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc); } else { desc = op_status2text(status); crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc); } if(send_trap && snmp_target) { send_snmp_trap(node, rsc, task, target_rc, rc, status, desc); } if(send_email && crm_mail_to) { send_smtp_trap(node, rsc, task, target_rc, rc, status, desc); } } void crm_diff_update(const char *event, xmlNode *msg) { int rc = -1; long now = time(NULL); const char *op = NULL; unsigned int log_level = LOG_INFO; xmlNode *diff = NULL; xmlNode *cib_last = NULL; xmlNode *update = get_message_xml(msg, F_CIB_UPDATE); print_dot(); if(msg == NULL) { crm_err("NULL update"); return; } crm_element_value_int(msg, F_CIB_RC, &rc); op = crm_element_value(msg, F_CIB_OPERATION); diff = get_message_xml(msg, F_CIB_UPDATE_RESULT); if(rc < cib_ok) { log_level = LOG_WARNING; do_crm_log(log_level, "[%s] %s ABORTED: %s", event, op, cib_error2string(rc)); return; } if(current_cib != NULL) { cib_last = current_cib; current_cib = NULL; rc = cib_process_diff(op, cib_force_diff, NULL, NULL, diff, cib_last, ¤t_cib, NULL); if(rc != cib_ok) { crm_debug("Update didn't apply, requesting full copy: %s", cib_error2string(rc)); free_xml(current_cib); current_cib = NULL; } } if(current_cib == NULL) { current_cib = get_cib_copy(cib); } if(log_diffs && diff) { log_cib_diff(LOG_DEBUG, diff, op); } if(log_updates && update != NULL) { print_xml_formatted(LOG_DEBUG, "raw_update", update, NULL); } if(diff && (crm_mail_to || snmp_target)) { /* Process operation updates */ xmlXPathObject *xpathObj = xpath_search( diff, "//"F_CIB_UPDATE_RESULT"//"XML_TAG_DIFF_ADDED"//"XML_LRM_TAG_RSC_OP); if(xpathObj && xpathObj->nodesetval->nodeNr > 0) { int lpc = 0, max = xpathObj->nodesetval->nodeNr; for(lpc = 0; lpc < max; lpc++) { xmlNode *rsc_op = getXpathResult(xpathObj, lpc); handle_rsc_op(rsc_op); } xmlXPathFreeObject(xpathObj); } } if((now - last_refresh) > (reconnect_msec/1000)) { /* Force a refresh */ mon_refresh_display(NULL); } else { mainloop_set_trigger(refresh_trigger); } free_xml(cib_last); } gboolean mon_refresh_display(gpointer user_data) { xmlNode *cib_copy = copy_xml(current_cib); pe_working_set_t data_set; last_refresh = time(NULL); if(cli_config_update(&cib_copy, NULL) == FALSE) { if(cib) { cib->cmds->signoff(cib); } print_as("Upgrade failed: %s", cib_error2string(cib_dtd_validation)); if(as_console) { sleep(2); } clean_up(LSB_EXIT_GENERIC); return FALSE; } set_working_set_defaults(&data_set); data_set.input = cib_copy; cluster_status(&data_set); if(as_html_file || web_cgi) { if (print_html_status(&data_set, as_html_file, web_cgi) != 0) { fprintf(stderr, "Critical: Unable to output html file\n"); clean_up(LSB_EXIT_GENERIC); } } else if(daemonize) { /* do nothing */ } else if (simple_status) { print_simple_status(&data_set); if (has_warnings) { clean_up(LSB_EXIT_GENERIC); } } else { print_status(&data_set); } cleanup_calculations(&data_set); return TRUE; } /* * De-init ncurses, signoff from the CIB and deallocate memory. */ void clean_up(int rc) { #if ENABLE_SNMP netsnmp_session *session = crm_snmp_init(NULL); if(session) { snmp_close(session); snmp_shutdown("snmpapp"); } #endif #if CURSES_ENABLED if(as_console) { as_console = FALSE; echo(); nocbreak(); endwin(); } #endif if (cib != NULL) { cib->cmds->signoff(cib); cib_delete(cib); cib = NULL; } crm_free(as_html_file); crm_free(xml_file); crm_free(pid_file); if(rc >= 0) { exit(rc); } return; } diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 0bd16d6182..d165789602 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -1,1436 +1,1439 @@ /* * 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 gboolean do_force = FALSE; gboolean BE_QUIET = FALSE; const char *attr_set_type = XML_TAG_ATTR_SETS; char *host_id = NULL; const char *rsc_id = NULL; const char *host_uname = NULL; const char *prop_name = NULL; const char *prop_value = NULL; const char *rsc_type = NULL; const char *prop_id = NULL; const char *prop_set = NULL; char *migrate_lifetime = NULL; char rsc_cmd = 'L'; char *our_pid = NULL; IPC_Channel *crmd_channel = NULL; char *xml_file = NULL; int cib_options = cib_sync_call; #define CMD_ERR(fmt, args...) do { \ crm_warn(fmt, ##args); \ fprintf(stderr, fmt, ##args); \ } while(0) static int do_find_resource(const char *rsc, pe_working_set_t *data_set) { int found = 0; resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if(the_rsc == NULL) { return cib_NOTEXISTS; } slist_iter(node, node_t, the_rsc->running_on, lpc, crm_debug_3("resource %s is running on: %s", rsc, node->details->uname); if(BE_QUIET) { fprintf(stdout, "%s\n", node->details->uname); } else { fprintf(stdout, "resource %s is running on: %s\n", rsc, node->details->uname); } found++; ); if(BE_QUIET == FALSE && found == 0) { fprintf(stderr, "resource %s is NOT running\n", rsc); } return 0; } #define cons_string(x) x?x:"NA" static void print_cts_constraints(pe_working_set_t *data_set) { xmlNode *lifetime = NULL; xmlNode * cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input); xml_child_iter(cib_constraints, xml_obj, const char *id = crm_element_value(xml_obj, XML_ATTR_ID); if(id == NULL) { continue; } lifetime = first_named_child(xml_obj, "lifetime"); if(test_ruleset(lifetime, NULL, data_set->now) == FALSE) { continue; } if(safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) { printf("Constraint %s %s %s %s %s %s %s\n", crm_element_name(xml_obj), cons_string(crm_element_value(xml_obj, XML_ATTR_ID)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)), cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE))); } else if(safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) { /* unpack_rsc_location(xml_obj, data_set); */ } ); } static void print_cts_rsc(resource_t *rsc) { const char *host = NULL; gboolean needs_quorum = TRUE; const char *p_id = "NA"; const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); const char *rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); if(rsc->parent) { p_id = rsc->parent->id; } if(safe_str_eq(rclass, "stonith")) { needs_quorum = FALSE; } else { xml_child_iter_filter(rsc->ops_xml, op, "op", const char *name = crm_element_value(op, "name"); if(safe_str_neq(name, CRMD_ACTION_START)) { const char *value = crm_element_value(op, "requires"); if(safe_str_eq(value, "nothing")) { needs_quorum = FALSE; } break; } ); } if(rsc->running_on != NULL && g_list_length(rsc->running_on) == 1) { node_t *tmp = rsc->running_on->data; host = tmp->details->uname; } printf("Resource: %s %s %s %s %d %d %d %s %s %s %s\n", crm_element_name(rsc->xml), rsc->id, rsc->clone_name?rsc->clone_name:rsc->id, p_id, is_set(rsc->flags, pe_rsc_managed), needs_quorum, is_set(rsc->flags, pe_rsc_unique), rprov?rprov:"NA", rclass, rtype, host?host:"NA"); slist_iter(child, resource_t, rsc->children, lpc, print_cts_rsc(child); ); } static void print_raw_rsc(resource_t *rsc) { GListPtr children = rsc->children; if(children == NULL) { printf("%s\n", rsc->id); } slist_iter(child, resource_t, children, lpc, print_raw_rsc(child); ); } static int do_find_resource_list(pe_working_set_t *data_set, gboolean raw) { int found = 0; slist_iter( rsc, resource_t, data_set->resources, lpc, if(is_set(rsc->flags, pe_rsc_orphan) && rsc->fns->active(rsc, TRUE) == FALSE) { continue; } rsc->fns->print( rsc, NULL, pe_print_printf|pe_print_rsconly, stdout); found++; ); if(found == 0) { printf("NO resources configured\n"); return cib_NOTEXISTS; } return 0; } static resource_t *find_rsc_or_clone(const char *rsc, pe_working_set_t *data_set) { resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if(the_rsc == NULL) { char *as_clone = crm_concat(rsc, "0", ':'); the_rsc = pe_find_resource(data_set->resources, as_clone); crm_free(as_clone); } return the_rsc; } static int dump_resource(const char *rsc, pe_working_set_t *data_set) { char *rsc_xml = NULL; resource_t *the_rsc = find_rsc_or_clone(rsc, data_set); if(the_rsc == NULL) { return cib_NOTEXISTS; } the_rsc->fns->print(the_rsc, NULL, pe_print_printf, stdout); rsc_xml = dump_xml_formatted(the_rsc->xml); fprintf(stdout, "raw xml:\n%s", rsc_xml); crm_free(rsc_xml); return 0; } static int dump_resource_attr( const char *rsc, const char *attr, pe_working_set_t *data_set) { int rc = cib_NOTEXISTS; node_t *current = NULL; GHashTable *params = NULL; resource_t *the_rsc = find_rsc_or_clone(rsc, data_set); const char *value = NULL; if(the_rsc == NULL) { return cib_NOTEXISTS; } if(g_list_length(the_rsc->running_on) == 1) { current = the_rsc->running_on->data; } else if(g_list_length(the_rsc->running_on) > 1) { CMD_ERR("%s is active on more than one node," " returning the default value for %s\n", the_rsc->id, crm_str(value)); } params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); get_rsc_attributes(params, the_rsc, current, data_set); crm_debug("Looking up %s in %s", attr, the_rsc->id); value = g_hash_table_lookup(params, attr); if(value != NULL) { fprintf(stdout, "%s\n", value); rc = 0; } g_hash_table_destroy(params); return rc; } static int find_resource_attr( cib_t *the_cib, const char *attr, const char *rsc, const char *set_type, const char *set_name, const char *attr_id, const char *attr_name, char **value) { int offset = 0; static int xpath_max = 1024; enum cib_errors rc = cib_ok; xmlNode *xml_search = NULL; char *xpath_string = NULL; CRM_ASSERT(value != NULL); *value = NULL; crm_malloc0(xpath_string, xpath_max); offset += snprintf(xpath_string + offset, xpath_max - offset, "%s", get_object_path("resources")); offset += snprintf(xpath_string + offset, xpath_max - offset, "//*[@id=\"%s\"]", rsc); if(set_type) { offset += snprintf(xpath_string + offset, xpath_max - offset, "//%s", set_type); if(set_name) { offset += snprintf(xpath_string + offset, xpath_max - offset, "[@id=\"%s\"]", set_name); } } offset += snprintf(xpath_string + offset, xpath_max - offset, "//nvpair["); if(attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, "@id=\"%s\"", attr_id); } if(attr_name) { if(attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, " and "); } offset += snprintf(xpath_string + offset, xpath_max - offset, "@name=\"%s\"", attr_name); } offset += snprintf(xpath_string + offset, xpath_max - offset, "]"); rc = the_cib->cmds->query( the_cib, xpath_string, &xml_search, cib_sync_call|cib_scope_local|cib_xpath); if(rc != cib_ok) { return rc; } crm_log_xml_debug(xml_search, "Match"); if(xml_has_children(xml_search)) { rc = cib_missing_data; printf("Multiple attributes match name=%s\n", attr_name); xml_child_iter(xml_search, child, printf(" Value: %s \t(id=%s)\n", crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); ); } else { const char *tmp = crm_element_value(xml_search, attr); if(tmp) { *value = crm_strdup(tmp); } } free_xml(xml_search); return rc; } static int set_resource_attr(const char *rsc_id, const char *attr_set, const char *attr_id, const char *attr_name, const char *attr_value, cib_t *cib, pe_working_set_t *data_set) { int rc = cib_ok; char *local_attr_id = NULL; char *local_attr_set = NULL; xmlNode *xml_top = NULL; xmlNode *xml_obj = NULL; gboolean use_attributes_tag = FALSE; resource_t *rsc = find_rsc_or_clone(rsc_id, data_set); if(rsc == NULL) { return cib_NOTEXISTS; } if(safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) { rc = find_resource_attr( cib, XML_ATTR_ID, rsc_id, XML_TAG_META_SETS, attr_set, attr_id, attr_name, &local_attr_id); if(rc == cib_ok) { printf("WARNING: There is already a meta attribute called %s (id=%s)\n", attr_name, local_attr_id); } } rc = find_resource_attr( cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name, &local_attr_id); if(rc == cib_ok) { crm_debug("Found a match for name=%s: id=%s", attr_name, local_attr_id); attr_id = local_attr_id; } else if(rc != cib_NOTEXISTS) { return rc; } else { const char *value = NULL; xmlNode *cib_top = NULL; const char *tag = crm_element_name(rsc->xml); rc = cib->cmds->query(cib, "/cib", &cib_top, cib_sync_call|cib_scope_local|cib_xpath|cib_no_children); value = crm_element_value(cib_top, "ignore_dtd"); if(value != NULL) { use_attributes_tag = TRUE; } else { value = crm_element_value(cib_top, XML_ATTR_VALIDATION); if(value && strstr(value, "-0.6")) { use_attributes_tag = TRUE; } } free_xml(cib_top); if(attr_set == NULL) { local_attr_set = crm_concat(rsc_id, attr_set_type, '-'); attr_set = local_attr_set; } if(attr_id == NULL) { local_attr_id = crm_concat(attr_set, attr_name, '-'); attr_id = local_attr_id; } if(use_attributes_tag && safe_str_eq(tag, XML_CIB_TAG_MASTER)) { tag = "master_slave"; /* use the old name */ } xml_top = create_xml_node(NULL, tag); crm_xml_add(xml_top, XML_ATTR_ID, rsc_id); xml_obj = create_xml_node(xml_top, attr_set_type); crm_xml_add(xml_obj, XML_ATTR_ID, attr_set); if(use_attributes_tag) { xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS); } } xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); if(xml_top == NULL) { xml_top = xml_obj; } crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); crm_log_xml_debug(xml_top, "Update"); rc = cib->cmds->modify(cib, XML_CIB_TAG_RESOURCES, xml_top, cib_options); free_xml(xml_top); crm_free(local_attr_id); crm_free(local_attr_set); return rc; } static int delete_resource_attr( const char *rsc_id, const char *attr_set, const char *attr_id, const char *attr_name, cib_t *cib, pe_working_set_t *data_set) { xmlNode *xml_obj = NULL; int rc = cib_ok; char *local_attr_id = NULL; resource_t *rsc = find_rsc_or_clone(rsc_id, data_set); if(rsc == NULL) { return cib_NOTEXISTS; } rc = find_resource_attr( cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name, &local_attr_id); if(rc == cib_NOTEXISTS) { return cib_ok; } else if(rc != cib_ok) { return rc; } if(attr_id == NULL) { attr_id = local_attr_id; } xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); crm_log_xml_debug(xml_obj, "Delete"); rc = cib->cmds->delete(cib, XML_CIB_TAG_RESOURCES, xml_obj, cib_options); if(rc == cib_ok) { printf("Deleted %s option: id=%s%s%s%s%s\n", rsc_id, local_attr_id, attr_set?" set=":"", attr_set?attr_set:"", attr_name?" name=":"", attr_name?attr_name:""); } free_xml(xml_obj); crm_free(local_attr_id); return rc; } static int dump_resource_prop( const char *rsc, const char *attr, pe_working_set_t *data_set) { const char *value = NULL; resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if(the_rsc == NULL) { return cib_NOTEXISTS; } value = crm_element_value(the_rsc->xml, attr); if(value != NULL) { fprintf(stdout, "%s\n", value); return 0; } return cib_NOTEXISTS; } static void resource_ipc_connection_destroy(gpointer user_data) { crm_info("Connection to CRMd was terminated"); exit(1); } static gboolean crmd_msg_callback(IPC_Channel * server, void *private_data) { int lpc = 0; IPC_Message *msg = NULL; gboolean hack_return_good = TRUE; while (server->ch_status != IPC_DISCONNECT && server->ops->is_message_pending(server) == TRUE) { if (server->ops->recv(server, &msg) != IPC_OK) { perror("Receive failure:"); return !hack_return_good; } if (msg == NULL) { crm_debug_4("No message this time"); continue; } lpc++; msg->msg_done(msg); } if (server->ch_status == IPC_DISCONNECT) { crm_debug_2("admin_msg_callback: received HUP"); return !hack_return_good; } return hack_return_good; } static int send_lrm_rsc_op(IPC_Channel *crmd_channel, const char *op, const char *host_uname, const char *rsc_id, gboolean only_failed, pe_working_set_t *data_set) { char *key = NULL; int rc = cib_send_failed; xmlNode *cmd = NULL; xmlNode *xml_rsc = NULL; const char *value = NULL; xmlNode *params = NULL; xmlNode *msg_data = NULL; resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); if(rsc == NULL) { CMD_ERR("Resource %s not found\n", rsc_id); return cib_NOTEXISTS; } else if(rsc->variant != pe_native) { CMD_ERR("We can only process primitive resources, not %s\n", rsc_id); return cib_invalid_argument; } else if(host_uname == NULL) { CMD_ERR("Please supply a hostname with -H\n"); return cib_invalid_argument; } key = crm_concat("0:0:crm-resource", our_pid, '-'); msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key); crm_free(key); xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE); if(rsc->clone_name) { crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->clone_name); crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->id); } else { crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->id); crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->long_name); } value = crm_element_value(rsc->xml, XML_ATTR_TYPE); crm_xml_add(xml_rsc, XML_ATTR_TYPE, value); if(value == NULL) { CMD_ERR("%s has no type! Aborting...\n", rsc_id); return cib_NOTEXISTS; } value = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, value); if(value == NULL) { CMD_ERR("%s has no class! Aborting...\n", rsc_id); return cib_NOTEXISTS; } value = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, value); params = create_xml_node(msg_data, XML_TAG_ATTRS); crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); key = crm_meta_name(XML_LRM_ATTR_INTERVAL); crm_xml_add(params, key, "60000"); /* 1 minute */ crm_free(key); cmd = create_request(op, msg_data, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); /* crm_log_xml_warn(cmd, "send_lrm_rsc_op"); */ free_xml(msg_data); if(send_ipc_message(crmd_channel, cmd)) { rc = 0; sleep(1); /* dont exit striaght away, give the crmd time * to process our request */ } else { CMD_ERR("Could not send %s op to the crmd", op); } free_xml(cmd); return rc; } static int delete_lrm_rsc(IPC_Channel *crmd_channel, const char *host_uname, const char *rsc_id, pe_working_set_t *data_set) { return send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_DELETE, host_uname, rsc_id, TRUE, data_set); } static int fail_lrm_rsc(IPC_Channel *crmd_channel, const char *host_uname, const char *rsc_id, pe_working_set_t *data_set) { crm_warn("Failing: %s", rsc_id); return send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_FAIL, host_uname, rsc_id, FALSE, data_set); } static int refresh_lrm(IPC_Channel *crmd_channel, const char *host_uname) { xmlNode *cmd = NULL; int rc = cib_send_failed; cmd = create_request(CRM_OP_LRM_REFRESH, NULL, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); if(send_ipc_message(crmd_channel, cmd)) { rc = 0; } free_xml(cmd); return rc; } static int migrate_resource( const char *rsc_id, const char *existing_node, const char *preferred_node, cib_t * cib_conn) { char *later_s = NULL; enum cib_errors rc = cib_ok; char *id = NULL; xmlNode *rule = NULL; xmlNode *expr = NULL; xmlNode *constraints = NULL; xmlNode *fragment = NULL; xmlNode *can_run = NULL; xmlNode *dont_run = NULL; fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); constraints = fragment; id = crm_concat("cli-prefer", rsc_id, '-'); can_run = create_xml_node(NULL, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(can_run, XML_ATTR_ID, id); crm_free(id); id = crm_concat("cli-standby", rsc_id, '-'); dont_run = create_xml_node(NULL, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(dont_run, XML_ATTR_ID, id); crm_free(id); if(migrate_lifetime) { char *life = crm_strdup(migrate_lifetime); char *life_mutable = life; ha_time_t *now = NULL; ha_time_t *later = NULL; ha_time_t *duration = parse_time_duration(&life_mutable); if(duration == NULL) { CMD_ERR("Invalid duration specified: %s\n", migrate_lifetime); CMD_ERR("Please refer to" " http://en.wikipedia.org/wiki/ISO_8601#Duration" " for examples of valid durations\n"); crm_free(life); return cib_invalid_argument; } now = new_ha_date(TRUE); later = add_time(now, duration); log_date(LOG_INFO, "now ", now, ha_log_date|ha_log_time); log_date(LOG_INFO, "later ", later, ha_log_date|ha_log_time); log_date(LOG_INFO, "duration", duration, ha_log_date|ha_log_time|ha_log_local); later_s = date_to_string(later, ha_log_date|ha_log_time); printf("Migration will take effect until: %s\n", later_s); free_ha_date(duration); free_ha_date(later); free_ha_date(now); crm_free(life); } if(existing_node == NULL) { crm_log_xml_notice(can_run, "Deleting"); rc = cib_conn->cmds->delete( cib_conn, XML_CIB_TAG_CONSTRAINTS, dont_run, cib_options); if(rc == cib_NOTEXISTS) { rc = cib_ok; } else if(rc != cib_ok) { goto bail; } } else { if(BE_QUIET == FALSE) { fprintf(stderr, "WARNING: Creating rsc_location constraint '%s'" " with a score of -INFINITY for resource %s" " on %s.\n", ID(dont_run), rsc_id, existing_node); CMD_ERR("\tThis will prevent %s from running" " on %s until the constraint is removed using" " the 'crm_resource -U' command or manually" " with cibadmin\n", rsc_id, existing_node); CMD_ERR("\tThis will be the case even if %s is" " the last node in the cluster\n", existing_node); CMD_ERR("\tThis message can be disabled with -Q\n"); } crm_xml_add(dont_run, "rsc", rsc_id); rule = create_xml_node(dont_run, XML_TAG_RULE); expr = create_xml_node(rule, XML_TAG_EXPRESSION); id = crm_concat("cli-standby-rule", rsc_id, '-'); crm_xml_add(rule, XML_ATTR_ID, id); crm_free(id); crm_xml_add(rule, XML_RULE_ATTR_SCORE, MINUS_INFINITY_S); crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); id = crm_concat("cli-standby-expr", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname"); crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); crm_xml_add(expr, XML_EXPR_ATTR_VALUE, existing_node); crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); if(later_s) { expr = create_xml_node(rule, "date_expression"); id = crm_concat("cli-standby-lifetime-end",rsc_id,'-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, "operation", "lt"); crm_xml_add(expr, "end", later_s); } add_node_copy(constraints, dont_run); } if(preferred_node == NULL) { crm_log_xml_notice(can_run, "Deleting"); rc = cib_conn->cmds->delete( cib_conn, XML_CIB_TAG_CONSTRAINTS, can_run, cib_options); if(rc == cib_NOTEXISTS) { rc = cib_ok; } else if(rc != cib_ok) { goto bail; } } else { crm_xml_add(can_run, "rsc", rsc_id); rule = create_xml_node(can_run, XML_TAG_RULE); expr = create_xml_node(rule, XML_TAG_EXPRESSION); id = crm_concat("cli-prefer-rule", rsc_id, '-'); crm_xml_add(rule, XML_ATTR_ID, id); crm_free(id); crm_xml_add(rule, XML_RULE_ATTR_SCORE, INFINITY_S); crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); id = crm_concat("cli-prefer-expr", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname"); crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); crm_xml_add(expr, XML_EXPR_ATTR_VALUE, preferred_node); crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); if(later_s) { expr = create_xml_node(rule, "date_expression"); id = crm_concat("cli-prefer-lifetime-end", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); crm_free(id); crm_xml_add(expr, "operation", "lt"); crm_xml_add(expr, "end", later_s); } add_node_copy(constraints, can_run); } if(preferred_node != NULL || existing_node != NULL) { crm_log_xml_notice(fragment, "CLI Update"); rc = cib_conn->cmds->update( cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options); } bail: free_xml(fragment); free_xml(dont_run); free_xml(can_run); crm_free(later_s); return rc; } static int list_resource_operations( const char *rsc_id, const char *host_uname, gboolean active, pe_working_set_t *data_set) { resource_t *rsc = NULL; int opts = pe_print_printf|pe_print_rsconly|pe_print_suppres_nl; GListPtr ops = find_operations(rsc_id, host_uname, active, data_set); slist_iter(xml_op, xmlNode, ops, lpc, const char *op_rsc = crm_element_value(xml_op, "resource"); const char *last = crm_element_value(xml_op, "last_run"); const char *status_s = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); int status = crm_parse_int(status_s, "0"); rsc = pe_find_resource(data_set->resources, op_rsc); rsc->fns->print(rsc, "", opts, stdout); fprintf(stdout, ": %s (node=%s, call=%s, rc=%s", ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), crm_element_value(xml_op, XML_LRM_ATTR_CALLID), crm_element_value(xml_op, XML_LRM_ATTR_RC)); if(last) { time_t run_at = crm_parse_int(last, "0"); fprintf(stdout, ", last-run=%s, exec=%sms\n", ctime(&run_at), crm_element_value(xml_op, "exec_time")); } fprintf(stdout, "): %s\n", op_status2text(status)); ); return cib_ok; } static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"quiet", 0, 0, 'Q', "Print only the value on stdout (for use with -W)"}, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, + {"help", 0, 0, '?', "\t\tThis text"}, + {"version", 0, 0, '$', "\t\tVersion information" }, + {"verbose", 0, 0, 'V', "\t\tIncrease debug output"}, + {"quiet", 0, 0, 'Q', "\t\tPrint only the value on stdout\n"}, {"resource", 1, 0, 'r', "\tResource ID" }, - {"resource-type", 1, 0, 't', "Resource type (primitive, clone, group, ...)"}, - {"node", 1, 0, 'N', "\tHost uname"}, - {"meta", 0, 0, 'm', "\t\tModify a resource's configuration option rather than one which is passed to the resource agent script. For use with -p, -g, -d"}, - {"property-value", 1, 0, 'v', "Value to use with -p, -g or -d"}, - {"force", 0, 0, 'f', "\n" /* Is this actually true anymore? - "\t\tForce the resource to move by creating a rule for the current location and a score of -INFINITY" - "\n\t\tThis should be used if the resource's stickiness and constraint scores total more than INFINITY (Currently 100,000)" - "\n\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U or the --lifetime duration expires\n"*/ }, - + + {"-spacer-", 1, 0, '-', "\nQueries:"}, {"list", 0, 0, 'L', "\t\tList all resources"}, - {"list-raw", 0, 0, 'l', "\t\tList the IDs of all instansiated resources (no groups/clones/...)"}, + {"list-raw", 0, 0, 'l', "\tList the IDs of all instansiated resources (no groups/clones/...)"}, {"list-cts", 0, 0, 'c', NULL, 1}, {"list-operations", 0, 0, 'O', "\tList active resource operations. Optionally filtered by resource (-r) and/or node (-N)"}, - {"list-all-operations", 0, 0, 'o', "List all resource operations. Optionally filtered by resource (-r) and/or node (-N)\n"}, + {"list-all-operations", 0, 0, 'o', "List all resource operations. Optionally filtered by resource (-r) and/or node (-N)\n"}, + {"query-xml", 0, 0, 'q', "\tQuery the definition of a resource"}, + {"locate", 0, 0, 'W', "\t\tDisplay the current location(s) of a resource"}, + + {"-spacer-", 1, 0, '-', "\nCommands:"}, + {"set-parameter", 1, 0, 'p', "Set the named parameter for a resource. See also -m, --meta"}, + {"get-parameter", 1, 0, 'g', "Display the named parameter for a resource. See also -m, --meta"}, + {"delete-parameter",1, 0, 'd', "Delete the named parameter for a resource. See also -m, --meta"}, + {"get-property", 1, 0, 'G', "Display the 'class', 'type' or 'provider' of a resource", 1}, + {"set-property", 1, 0, 'S', "(Advanced) Set the class, type or provider of a resource", 1}, + {"migrate", 0, 0, 'M', + "\t\tMigrate a resource from its current location, optionally specifying a destination (-N) and/or a period for which it should take effect (-u)" + "\n\t\t\t\tIf -N is not specified, the cluster will force the resource to move by creating a rule for the current location and a score of -INFINITY" + "\n\t\t\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U"}, + {"un-migrate", 0, 0, 'U', "\tRemove all constraints created by a migrate command"}, - {"query-xml", 0, 0, 'q', "Query the definition of a resource"}, - {"locate", 0, 0, 'W', "Display the current location(s) of a resource"}, - {"delete", 0, 0, 'D', "Delete a resource from the CIB"}, - {"fail", 0, 0, 'F', "Tell the cluster this resource has failed\n"}, - - {"refresh", 0, 0, 'R', "(Advanced) Refresh the CIB from the LRM"}, - {"cleanup", 0, 0, 'C', "(Advanced) Delete a resource from the LRM"}, - {"reprobe", 0, 0, 'P', "(Advanced) Re-check for resources started outside of the CRM\n"}, + {"-spacer-", 1, 0, '-', "\nAdvanced Commands:"}, + {"delete", 0, 0, 'D', "\t\tDelete a resource from the CIB"}, + {"fail", 0, 0, 'F', "\t\tTell the cluster this resource has failed"}, + {"refresh", 0, 0, 'R', "\t\t(Advanced) Refresh the CIB from the LRM"}, + {"cleanup", 0, 0, 'C', "\t\t(Advanced) Delete a resource from the LRM"}, + {"reprobe", 0, 0, 'P', "\t\t(Advanced) Re-check for resources started outside of the CRM\n"}, - {"migrate", 0, 0, 'M', - "Migrate a resource from its current location, optionally specifying a destination (-N) and/or a period for which it should take effect (-u)" - "\n\t\tIf -N is not specified, the cluster will force the resource to move by creating a rule for the current location and a score of -INFINITY" - "\n\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U"}, - {"un-migrate", 0, 0, 'U', "Remove all constraints created by a migrate command"}, - {"lifetime", 1, 0, 'u', "Lifespan of migration constraints\n"}, - - {"set-parameter", 1, 0, 'p', "Set the named parameter for a resource. See also -m|--meta"}, - {"get-parameter", 1, 0, 'g', "Display the named parameter for a resource. See also -m|--meta"}, - {"delete-parameter",1, 0, 'd', "Delete the named parameter for a resource. See also -m|--meta"}, - {"get-property", 1, 0, 'G', "Display the 'class', 'type' or 'provider' of a resource"}, - {"set-property", 1, 0, 'S', "(Advanced) Set the class, type or provider of a resource"}, + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {"node", 1, 0, 'N', "\tHost uname"}, + {"resource-type", 1, 0, 't', "Resource type (primitive, clone, group, ...)"}, + {"property-value", 1, 0, 'v', "Value to use with -p, -g or -d"}, + {"lifetime", 1, 0, 'u', "\tLifespan of migration constraints\n"}, + {"meta", 0, 0, 'm', "\t\tModify a resource's configuration option rather than one which is passed to the resource agent script. For use with -p, -g, -d"}, {"set-name", 1, 0, 's', "\t(Advanced) ID of the instance_attributes object to change"}, {"nvpair", 1, 0, 'i', "\t(Advanced) ID of the nvpair object to change/delete"}, + {"force", 0, 0, 'f', "\n" /* Is this actually true anymore? + "\t\tForce the resource to move by creating a rule for the current location and a score of -INFINITY" + "\n\t\tThis should be used if the resource's stickiness and constraint scores total more than INFINITY (Currently 100,000)" + "\n\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U or the --lifetime duration expires\n"*/ }, {"xml-file", 0, 0, 'x', NULL, 1},\ /* legacy options */ {"host-uname", 1, 0, 'H', NULL, 1}, {0, 0, 0, 0} }; int main(int argc, char **argv) { pe_working_set_t data_set; xmlNode *cib_xml_copy = NULL; cib_t * cib_conn = NULL; enum cib_errors rc = cib_ok; int option_index = 0; int argerr = 0; int flag; crm_log_init(basename(argv[0]), LOG_ERR, FALSE, FALSE, argc, argv); - crm_set_options("V?$LRQxDCPp:WMUr:H:h:v:t:p:g:d:i:s:G:S:fx:lmu:FOocqN:", "[-?!VQ] -(L|l|O|o|q|W|D|F|R|C|P|M|U|p|g|d) [options]", long_options, + crm_set_options("V?$LRQxDCPp:WMUr:H:h:v:t:p:g:d:i:s:G:S:fx:lmu:FOocqN:", "(query|command) [options]", long_options, "Perform tasks related to cluster resources.\n Allows resources to be queried (definition and location), modified, and moved around the cluster.\n"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'x': xml_file = crm_strdup(optarg); break; case 'Q': BE_QUIET = TRUE; break; case 'm': attr_set_type = XML_TAG_META_SETS; break; case 'L': case 'c': case 'l': case 'R': case 'q': case 'D': case 'F': case 'C': case 'P': case 'W': case 'M': case 'U': rsc_cmd = flag; break; case 'u': migrate_lifetime = crm_strdup(optarg); break; case 'p': crm_debug_2("Option %c => %s", flag, optarg); prop_name = optarg; rsc_cmd = flag; break; case 'g': crm_debug_2("Option %c => %s", flag, optarg); prop_name = optarg; rsc_cmd = flag; break; case 'd': crm_debug_2("Option %c => %s", flag, optarg); prop_name = optarg; rsc_cmd = flag; break; case 'S': crm_debug_2("Option %c => %s", flag, optarg); prop_name = optarg; rsc_cmd = flag; break; case 'O': case 'o': crm_debug_2("Option %c => %s", flag, optarg); rsc_cmd = flag; break; case 'G': crm_debug_2("Option %c => %s", flag, optarg); prop_name = optarg; rsc_cmd = flag; break; case 'f': do_force = TRUE; break; case 'i': crm_debug_2("Option %c => %s", flag, optarg); prop_id = optarg; break; case 's': crm_debug_2("Option %c => %s", flag, optarg); prop_set = optarg; break; case 'r': crm_debug_2("Option %c => %s", flag, optarg); rsc_id = optarg; break; case 'v': crm_debug_2("Option %c => %s", flag, optarg); prop_value = optarg; break; case 't': crm_debug_2("Option %c => %s", flag, optarg); rsc_type = optarg; break; case 'h': case 'H': case 'N': crm_debug_2("Option %c => %s", flag, optarg); host_uname = optarg; break; default: CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc && argv[optind] != NULL) { CMD_ERR("non-option ARGV-elements: "); while (optind < argc && argv[optind] != NULL) { CMD_ERR("%s ", argv[optind++]); ++argerr; } CMD_ERR("\n"); } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } crm_malloc0(our_pid, 11); if(our_pid != NULL) { snprintf(our_pid, 10, "%d", getpid()); our_pid[10] = '\0'; } if(do_force) { crm_debug("Forcing..."); cib_options |= cib_scope_local|cib_quorum_override; } if(rsc_cmd == 'L' || rsc_cmd == 'O' || rsc_cmd == 'o' || rsc_cmd == 'W' || rsc_cmd == 'D' || rsc_cmd == 'q' || rsc_cmd == 'M' || rsc_cmd == 'U' || rsc_cmd == 'C' || rsc_cmd == 'F' || rsc_cmd == 'p' || rsc_cmd == 'd' || rsc_cmd == 'g' || rsc_cmd == 'G' || rsc_cmd == 'S' || rsc_cmd == 'c' || rsc_cmd == 'l') { resource_t *rsc = NULL; if(xml_file != NULL) { cib_xml_copy = filename2xml(xml_file); } else { cib_conn = cib_new(); rc = cib_conn->cmds->signon( cib_conn, crm_system_name, cib_command); if(rc != cib_ok) { CMD_ERR("Error signing on to the CIB service: %s\n", cib_error2string(rc)); return rc; } cib_xml_copy = get_cib_copy(cib_conn); } set_working_set_defaults(&data_set); if(cli_config_update(&cib_xml_copy, NULL) == FALSE) { return cib_STALE; } data_set.input = cib_xml_copy; data_set.now = new_ha_date(TRUE); cluster_status(&data_set); if(rsc_id) { rsc = find_rsc_or_clone(rsc_id, &data_set); } if(rsc == NULL) { rc = cib_NOTEXISTS; } } if(rsc_cmd == 'R' || rsc_cmd == 'C' || rsc_cmd == 'F' || rsc_cmd == 'P') { GCHSource *src = NULL; src = init_client_ipc_comms(CRM_SYSTEM_CRMD, crmd_msg_callback, NULL, &crmd_channel); if(src == NULL) { CMD_ERR("Error signing on to the CRMd service\n"); return 1; } send_hello_message( crmd_channel, our_pid, crm_system_name, "0", "1"); set_IPC_Channel_dnotify(src, resource_ipc_connection_destroy); } if(rsc_cmd == 'L') { rc = cib_ok; do_find_resource_list(&data_set, FALSE); } else if(rsc_cmd == 'l') { int found = 0; rc = cib_ok; slist_iter( rsc, resource_t, data_set.resources, lpc, found++; print_raw_rsc(rsc); ); if(found == 0) { printf("NO resources configured\n"); return cib_NOTEXISTS; } } else if(rsc_cmd == 'c') { int found = 0; rc = cib_ok; slist_iter( rsc, resource_t, data_set.resources, lpc, found++; print_cts_rsc(rsc); ); print_cts_constraints(&data_set); } else if(rsc_cmd == 'C') { resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); if(rsc && rsc->variant != pe_native) { fprintf(stderr, "We can only clean up primitive resources and %s is a %s\n", rsc_id, get_resource_typename(rsc->variant)); rc = cib_NOTEXISTS; } else { rc = delete_lrm_rsc(crmd_channel, host_uname, rsc_id, &data_set); } if(rc == cib_ok) { char *host_uuid = NULL; char *attr_name = crm_concat("fail-count", rsc_id, '-'); rc = query_node_uuid(cib_conn, host_uname, &host_uuid); if(rc != cib_ok) { fprintf(stderr,"Could not map uname=%s to a UUID: %s\n", host_uname, cib_error2string(rc)); } else { crm_info("Mapped %s to %s", host_uname, crm_str(host_uuid)); rc = delete_attr(cib_conn, cib_sync_call, XML_CIB_TAG_STATUS, host_uuid, NULL, NULL, attr_name, NULL, FALSE); } } } else if(rsc_cmd == 'F') { rc = fail_lrm_rsc(crmd_channel, host_uname, rsc_id, &data_set); } else if(rsc_cmd == 'O') { rc = list_resource_operations(rsc_id, host_uname, TRUE, &data_set); } else if(rsc_cmd == 'o') { rc = list_resource_operations(rsc_id, host_uname, FALSE, &data_set); } else if(rc == cib_NOTEXISTS) { CMD_ERR("Resource %s not found: %s\n", crm_str(rsc_id), cib_error2string(rc)); } else if(rsc_cmd == 'W') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } rc = do_find_resource(rsc_id, &data_set); } else if(rsc_cmd == 'q') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } rc = dump_resource(rsc_id, &data_set); } else if(rsc_cmd == 'U') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } rc = migrate_resource(rsc_id, NULL, NULL, cib_conn); } else if(rsc_cmd == 'M') { node_t *dest = NULL; node_t *current = NULL; const char *current_uname = NULL; resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); if(rsc != NULL && rsc->running_on != NULL) { current = rsc->running_on->data; if(current != NULL) { current_uname = current->details->uname; } } if(host_uname != NULL) { dest = pe_find_node(data_set.nodes, host_uname); } if(rsc == NULL) { CMD_ERR("Resource %s not migrated:" " not found\n", rsc_id); } else if(rsc->variant == pe_native && g_list_length(rsc->running_on) > 1) { CMD_ERR("Resource %s not migrated:" " active on multiple nodes\n", rsc_id); } else if(host_uname != NULL && dest == NULL) { CMD_ERR("Error performing operation: " "%s is not a known node\n", host_uname); rc = cib_NOTEXISTS; } else if(host_uname != NULL && safe_str_eq(current_uname, host_uname)) { CMD_ERR("Error performing operation: " "%s is already active on %s\n", rsc_id, host_uname); } else if(current_uname != NULL && (do_force || host_uname == NULL)) { rc = migrate_resource(rsc_id, current_uname, host_uname, cib_conn); } else if(host_uname != NULL) { rc = migrate_resource( rsc_id, NULL, host_uname, cib_conn); } else { CMD_ERR("Resource %s not migrated: " "not-active and no prefered location" " specified.\n", rsc_id); rc = cib_missing; } } else if(rsc_cmd == 'G') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } rc = dump_resource_prop(rsc_id, prop_name, &data_set); } else if(rsc_cmd == 'S') { xmlNode *msg_data = NULL; if(prop_value == NULL || strlen(prop_value) == 0) { CMD_ERR("You need to supply a value with the -v option\n"); return CIBRES_MISSING_FIELD; } else if(cib_conn == NULL) { return cib_connection; } if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } CRM_DEV_ASSERT(rsc_type != NULL); CRM_DEV_ASSERT(prop_name != NULL); CRM_DEV_ASSERT(prop_value != NULL); msg_data = create_xml_node(NULL, rsc_type); crm_xml_add(msg_data, XML_ATTR_ID, rsc_id); crm_xml_add(msg_data, prop_name, prop_value); rc = cib_conn->cmds->modify( cib_conn, XML_CIB_TAG_RESOURCES, msg_data, cib_options); free_xml(msg_data); } else if(rsc_cmd == 'g') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } rc = dump_resource_attr(rsc_id, prop_name, &data_set); } else if(rsc_cmd == 'p') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } if(prop_value == NULL || strlen(prop_value) == 0) { CMD_ERR("You need to supply a value with the -v option\n"); return CIBRES_MISSING_FIELD; } rc = set_resource_attr(rsc_id, prop_set, prop_id, prop_name, prop_value, cib_conn, &data_set); } else if(rsc_cmd == 'd') { if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } rc = delete_resource_attr(rsc_id, prop_set, prop_id, prop_name, cib_conn, &data_set); } else if(rsc_cmd == 'P') { xmlNode *cmd = NULL; cmd = create_request(CRM_OP_REPROBE, NULL, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); send_ipc_message(crmd_channel, cmd); free_xml(cmd); } else if(rsc_cmd == 'R') { refresh_lrm(crmd_channel, host_uname); } else if(rsc_cmd == 'D') { xmlNode *msg_data = NULL; if(rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); return cib_NOTEXISTS; } if(rsc_type == NULL) { CMD_ERR("You need to specify a resource type with -t"); return cib_NOTEXISTS; } else if(cib_conn == NULL) { return cib_connection; } msg_data = create_xml_node(NULL, rsc_type); crm_xml_add(msg_data, XML_ATTR_ID, rsc_id); rc = cib_conn->cmds->delete( cib_conn, XML_CIB_TAG_RESOURCES, msg_data, cib_options); free_xml(msg_data); } else { CMD_ERR("Unknown command: %c\n", rsc_cmd); } if(cib_conn != NULL) { cleanup_calculations(&data_set); cib_conn->cmds->signoff(cib_conn); } if(rc == cib_no_quorum) { CMD_ERR("Error performing operation: %s\n", cib_error2string(rc)); CMD_ERR("Try using -f\n"); } else if(rc != cib_ok) { CMD_ERR("Error performing operation: %s\n", cib_error2string(rc)); } return rc; } diff --git a/tools/crm_verify.c b/tools/crm_verify.c index 0eec4571e0..93c4dddd1e 100644 --- a/tools/crm_verify.c +++ b/tools/crm_verify.c @@ -1,286 +1,288 @@ /* * 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 gboolean USE_LIVE_CIB = FALSE; char *cib_save = NULL; void usage(const char *cmd, int exit_status); extern gboolean stage0(pe_working_set_t *data_set); extern void cleanup_alloc_calculations(pe_working_set_t *data_set); extern xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, - {"xml-text", 1, 0, 'X', "Check the configuration in the supplied string"}, - {"xml-file", 1, 0, 'x', "Check the configuration in the named file"}, - {"xml-pipe", 0, 0, 'p', "\tCheck the configuration piped in via stdin"}, + {"-spacer-", 1, 0, '-', "\nData sources:"}, {"live-check", 0, 0, 'L', "Check the configuration used by the running cluster\n"}, + {"xml-file", 1, 0, 'x', "Check the configuration in the named file"}, + {"xml-text", 1, 0, 'X', "Check the configuration in the supplied string"}, + {"xml-pipe", 0, 0, 'p', "Check the configuration piped in via stdin"}, - {"save-xml", 1, 0, 'S'}, + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {"save-xml", 1, 0, 'S', "Save the verified XML to the named file. Most useful with -L"}, {F_CRM_DATA, 1, 0, 'X', NULL, 1}, /* legacy */ {0, 0, 0, 0} }; int main(int argc, char **argv) { xmlNode *cib_object = NULL; xmlNode *status = NULL; int argerr = 0; int flag; int option_index = 0; pe_working_set_t data_set; cib_t * cib_conn = NULL; int rc = cib_ok; gboolean xml_stdin = FALSE; const char *xml_tag = NULL; const char *xml_file = NULL; const char *xml_string = NULL; g_log_set_handler(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL, cl_glib_msg_handler, NULL); /* and for good measure... - this enum is a bit field (!) */ g_log_set_always_fatal((GLogLevelFlags)0); /*value out of range*/ crm_log_init(basename(argv[0]), LOG_ERR, FALSE, TRUE, 0, NULL); - crm_set_options("V?$X:x:pLS:", NULL, long_options, + crm_set_options("V?$X:x:pLS:", "data_source [modifiers]", long_options, "Check a (complete) confiuration for syntax and common conceptual errors.\n" " Checks the well-formedness of an XML configuration, its conformance to the configured DTD/schema and for the presence of common misconfigurations.\n"); while (1) { flag = crm_get_option(argc, argv, &option_index); 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"); break; #endif case 'X': crm_debug_2("Option %c => %s", flag, optarg); xml_string = crm_strdup(optarg); break; case 'x': crm_debug_2("Option %c => %s", flag, optarg); xml_file = crm_strdup(optarg); break; case 'p': xml_stdin = TRUE; break; case 'S': cib_save = crm_strdup(optarg); break; case 'V': alter_debug(DEBUG_INC); break; case 'L': USE_LIVE_CIB = TRUE; break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; default: fprintf(stderr, "Option -%c is not yet supported\n", 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) { crm_err("%d errors in option parsing", argerr); crm_help(flag, LSB_EXIT_GENERIC); } crm_info("=#=#=#=#= Getting XML =#=#=#=#="); if(USE_LIVE_CIB) { cib_conn = cib_new(); rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); } if(USE_LIVE_CIB) { if(rc == cib_ok) { int options = cib_scope_local|cib_sync_call; crm_info("Reading XML from: live cluster"); rc = cib_conn->cmds->query( cib_conn, NULL, &cib_object, options); } if(rc != cib_ok) { fprintf(stderr, "Live CIB query failed: %s\n", cib_error2string(rc)); return 3; } if(cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); return 3; } } else if(xml_file != NULL) { cib_object = filename2xml(xml_file); if(cib_object == NULL) { fprintf(stderr, "Couldn't parse input file: %s\n", xml_file); return 4; } } else if(xml_string != NULL) { cib_object = string2xml(xml_string); if(cib_object == NULL) { fprintf(stderr, "Couldn't parse input string: %s\n", xml_string); return 4; } } else if(xml_stdin) { cib_object = stdin2xml(); if(cib_object == NULL) { fprintf(stderr, "Couldn't parse input from STDIN.\n"); return 4; } } else { fprintf(stderr, "No configuration source specified." " Use --help for usage information.\n"); return 5; } xml_tag = crm_element_name(cib_object); if(safe_str_neq(xml_tag, XML_TAG_CIB)) { fprintf(stderr, "This tool can only check complete configurations (ie. those starting with ).\n"); return 6; } if(cib_save != NULL) { write_xml_file(cib_object, cib_save, FALSE); } status = get_object_root(XML_CIB_TAG_STATUS, cib_object); if(status == NULL) { create_xml_node(cib_object, XML_CIB_TAG_STATUS); } #if CRM_DEPRECATED_SINCE_2_0_4 xml_child_iter_filter(status, node_state, XML_CIB_TAG_STATE, xml_remove_prop(node_state, XML_CIB_TAG_LRM); ); #endif if(validate_xml(cib_object, NULL, FALSE) == FALSE) { crm_config_err("CIB did not pass DTD/schema validation"); free_xml(cib_object); cib_object = NULL; } else if(cli_config_update(&cib_object, NULL) == FALSE) { crm_config_error = TRUE; free_xml(cib_object); cib_object = NULL; fprintf(stderr, "The cluster will NOT be able to use this configuration.\n"); fprintf(stderr, "Please manually update the configuration to conform to the %s syntax.\n", LATEST_SCHEMA_VERSION); } if(cib_object == NULL) { } else if(USE_LIVE_CIB) { /* we will always have a status section and can do a full simulation */ do_calculations(&data_set, cib_object, NULL); cleanup_alloc_calculations(&data_set); } else { set_working_set_defaults(&data_set); data_set.now = new_ha_date(TRUE); data_set.input = cib_object; stage0(&data_set); cleanup_alloc_calculations(&data_set); } if(crm_config_error) { fprintf(stderr, "Errors found during check: config not valid\n"); if(crm_log_level < LOG_WARNING) { fprintf(stderr, " -V may provide more details\n"); } rc = 2; } else if(crm_config_warning) { fprintf(stderr, "Warnings found during check: config may not be valid\n"); if(crm_log_level < LOG_WARNING) { fprintf(stderr, " Use -V for more details\n"); } rc = 1; } if(USE_LIVE_CIB) { cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); } return rc; } diff --git a/tools/crmadmin.c b/tools/crmadmin.c index 319a1cc3c9..e1dabf0696 100644 --- a/tools/crmadmin.c +++ b/tools/crmadmin.c @@ -1,590 +1,592 @@ /* * 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 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); gboolean do_init(void); int do_work(void); void crmadmin_ipc_connection_destroy(gpointer user_data); gboolean admin_msg_callback(IPC_Channel * source_data, void *private_data); char *pluralSection(const char *a_section); xmlNode *handleCibMod(void); int do_find_node_list(xmlNode *xml_node); gboolean admin_message_timeout(gpointer data); gboolean is_node_online(xmlNode *node_state); enum debug { debug_none, debug_dec, debug_inc }; gboolean BE_VERBOSE = FALSE; int expected_responses = 1; gboolean BASH_EXPORT = FALSE; 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; enum debug DO_DEBUG = debug_none; const char *crmd_operation = NULL; xmlNode *msg_options = NULL; const char *standby_on_off = "on"; const char *admin_verbose = XML_BOOLEAN_FALSE; char *id = 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; static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"quiet", 0, 0, 'q', "Display only the essential query information"}, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, - - {XML_ATTR_TIMEOUT, 1, 0, 't', "Time (in milliseconds) to wait before declaring the operation failed"}, - {"bash-export", 0, 0, 'B', "Create Bash export entries of the form 'export uname=uuid'\n"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"quiet", 0, 0, 'q', "\tDisplay only the essential query information"}, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, + {"-spacer-", 1, 0, '-', "\nCommands:"}, /* daemon options */ {"debug_inc", 1, 0, 'i', "Increase the crmd's debug level on the specified host"}, {"debug_dec", 1, 0, 'd', "Decrease the crmd's debug level on the specified host"}, - {"status", 1, 0, 'S', "Display the status of the specified node"}, - {"dc_lookup", 0, 0, 'D', "\tDisplay the uname of the node co-ordinating the cluster"}, - {"nodes", 0, 0, 'N', "\tDisplay the uname of all member nodes"}, - {"election", 0, 0, 'E', "\t(Advanced) Start an election for the cluster co-ordinator"}, - {"kill", 1, 0, 'K', "(Advanced) Shut down the crmd (not the rest of the cluster) on the specified node"}, - {"health", 0, 0, 'H', NULL, 1}, + {"status", 1, 0, 'S', "Display the status of the specified node"}, + {"dc_lookup", 0, 0, 'D', "Display the uname of the node co-ordinating the cluster"}, + {"nodes", 0, 0, 'N', "\tDisplay the uname of all member nodes"}, + {"election", 0, 0, 'E', "(Advanced) Start an election for the cluster co-ordinator"}, + {"kill", 1, 0, 'K', "(Advanced) Shut down the crmd (not the rest of the cluster) on the specified node"}, + {"health", 0, 0, 'H', NULL, 1}, + + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {XML_ATTR_TIMEOUT, 1, 0, 't', "Time (in milliseconds) to wait before declaring the operation failed"}, + {"bash-export", 0, 0, 'B', "Create Bash export entries of the form 'export uname=uuid'\n"}, {0, 0, 0, 0} }; int main(int argc, char **argv) { int option_index = 0; int argerr = 0; int flag; crm_log_init(basename(argv[0]), LOG_ERR, FALSE, TRUE, argc, argv); - crm_set_options("V?$K:S:HE:Dd:i:Nqt:B", NULL, long_options, + crm_set_options("V?$K:S:HE:Dd:i:Nqt:B", "command [options]", long_options, "Development tool for performing some crmd-specific commands." "\n Likely to be replaced by crm_node in the future" ); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'V': BE_VERBOSE = TRUE; admin_verbose = XML_BOOLEAN_TRUE; cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case 't': message_timeout_ms = atoi(optarg); if(message_timeout_ms < 1) { message_timeout_ms = 30*1000; } break; case '$': case '?': crm_help(flag, LSB_EXIT_OK); break; case 'D': DO_WHOIS_DC = TRUE; break; case 'B': BASH_EXPORT = TRUE; break; case 'K': DO_RESET = TRUE; crm_debug_2("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); crmd_operation = CRM_OP_LOCAL_SHUTDOWN; break; case 'q': BE_SILENT = TRUE; break; case 'i': DO_DEBUG = debug_inc; crm_debug_2("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'd': DO_DEBUG = debug_dec; crm_debug_2("Option %c => %s", flag, optarg); dest_node = crm_strdup(optarg); break; case 'S': DO_HEALTH = TRUE; crm_debug_2("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 '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) { crm_help('?', LSB_EXIT_GENERIC); } if (do_init()) { int res = 0; res = do_work(); if (res > 0) { /* wait for the reply by creating a mainloop and running it until * the callbacks are invoked... */ mainloop = g_main_new(FALSE); expected_responses++; crm_debug_2("Waiting for %d replies from the local CRM", expected_responses); message_timer_id = g_timeout_add( message_timeout_ms, admin_message_timeout, NULL); g_main_run(mainloop); } else if(res < 0) { crm_err("No message to send"); operation_status = -1; } } else { crm_warn("Init failed, could not perform requested operations"); operation_status = -2; } crm_debug_2("%s exiting normally", crm_system_name); return operation_status; } int do_work(void) { int ret = 1; /* construct the request */ xmlNode *msg_data = NULL; gboolean all_is_good = TRUE; msg_options = create_xml_node(NULL, XML_TAG_OPTIONS); crm_xml_add(msg_options, XML_ATTR_VERBOSE, admin_verbose); crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); if (DO_HEALTH == TRUE) { crm_debug_2("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; } crm_xml_add(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; crm_xml_add(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; crm_xml_add(msg_options, XML_ATTR_TIMEOUT, "0"); dest_node = NULL; } else if(DO_NODE_LIST) { cib_t * the_cib = cib_new(); xmlNode *output = NULL; enum cib_errors rc = the_cib->cmds->signon( the_cib, crm_system_name, cib_command); if(rc != cib_ok) { return -1; } output = get_cib_copy(the_cib); do_find_node_list(output); free_xml(output); the_cib->cmds->signoff(the_cib); exit(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; crm_xml_add(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; } } { xmlNode *cmd = create_request( crmd_operation, msg_data, dest_node, sys_to, crm_system_name, admin_uuid); send_ipc_message(crmd_channel, cmd); free_xml(cmd); } return ret; } void crmadmin_ipc_connection_destroy(gpointer user_data) { crm_err("Connection to CRMd was terminated"); if(mainloop) { g_main_quit(mainloop); } else { exit(1); } } gboolean do_init(void) { GCHSource *src = NULL; crm_malloc0(admin_uuid, 11); if(admin_uuid != NULL) { snprintf(admin_uuid, 10, "%d", getpid()); admin_uuid[10] = '\0'; } src = init_client_ipc_comms( CRM_SYSTEM_CRMD, admin_msg_callback, NULL, &crmd_channel); if(DO_RESOURCE || DO_RESOURCE_LIST || DO_NODE_LIST) { return TRUE; } else if(crmd_channel != NULL) { send_hello_message( crmd_channel, admin_uuid, crm_system_name,"0", "1"); set_IPC_Channel_dnotify(src, crmadmin_ipc_connection_destroy); return TRUE; } return FALSE; } gboolean admin_msg_callback(IPC_Channel * server, void *private_data) { int rc = 0; int lpc = 0; xmlNode *xml = NULL; IPC_Message *msg = NULL; gboolean hack_return_good = TRUE; static int received_responses = 0; const char *result = NULL; g_source_remove(message_timer_id); while (server->ch_status != IPC_DISCONNECT && server->ops->is_message_pending(server) == TRUE) { rc = server->ops->recv(server, &msg); if (rc != IPC_OK) { crm_perror(LOG_ERR,"Receive failure (%d)", rc); return !hack_return_good; } if (msg == NULL) { crm_debug_4("No message this time"); continue; } lpc++; received_responses++; xml = convert_ipc_message(msg, __FUNCTION__); msg->msg_done(msg); crm_log_xml(LOG_MSG, "ipc", xml); if (xml == NULL) { crm_info("XML in IPC message was not valid... " "discarding."); goto cleanup; } else if (validate_crm_message( xml, crm_system_name, admin_uuid, XML_ATTR_RESPONSE) == FALSE) { crm_debug_2("Message was not a CRM response. Discarding."); goto cleanup; } result = crm_element_value(xml, XML_ATTR_RESULT); if(result == NULL || strcasecmp(result, "ok") == 0) { result = "pass"; } else { result = "fail"; } if(DO_HEALTH) { xmlNode *data = get_message_xml(xml, F_CRM_DATA); const char *state = crm_element_value(data, "crmd_state"); printf("Status of %s@%s: %s (%s)\n", crm_element_value(data,XML_PING_ATTR_SYSFROM), crm_element_value(xml, F_CRM_HOST_FROM), state, crm_element_value(data,XML_PING_ATTR_STATUS)); if(BE_SILENT && state != NULL) { fprintf(stderr, "%s\n", state); } } else if(DO_WHOIS_DC) { const char *dc = crm_element_value(xml, F_CRM_HOST_FROM); printf("Designated Controller is: %s\n", dc); if(BE_SILENT && dc != NULL) { fprintf(stderr, "%s\n", dc); } } cleanup: free_xml(xml); xml = NULL; } if (server->ch_status == IPC_DISCONNECT) { crm_debug_2("admin_msg_callback: received HUP"); return !hack_return_good; } if (received_responses >= expected_responses) { crm_debug_2( "Recieved expected number (%d) of messages from Heartbeat." " Exiting normally.", expected_responses); exit(0); } message_timer_id = g_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); operation_status = -3; g_main_quit(mainloop); return FALSE; } gboolean is_node_online(xmlNode *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 *exp_state = crm_element_value(node_state,XML_CIB_ATTR_EXPSTATE); 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_neq(join_state, CRMD_JOINSTATE_DOWN) && (ha_state == NULL || safe_str_eq(ha_state, "active")) && crm_is_true(ccm_state) && safe_str_eq(crm_state, "online")) { crm_debug_3("Node %s is online", uname); return TRUE; } crm_debug_3("Node %s: ha=%s ccm=%s join=%s exp=%s crm=%s", uname, crm_str(ha_state), crm_str(ccm_state), crm_str(join_state), crm_str(exp_state), crm_str(crm_state)); crm_debug_3("Node %s is offline", uname); return FALSE; } int do_find_node_list(xmlNode *xml_node) { int found = 0; xmlNode *nodes = get_object_root(XML_CIB_TAG_NODES, xml_node); xml_child_iter_filter( nodes, node, XML_CIB_TAG_NODE, if(BASH_EXPORT) { printf("export %s=%s\n", crm_element_value(node, XML_ATTR_UNAME), crm_element_value(node, XML_ATTR_ID)); } else { 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; } diff --git a/tools/test.iso8601.c b/tools/test.iso8601.c index 7787442313..d84555a017 100644 --- a/tools/test.iso8601.c +++ b/tools/test.iso8601.c @@ -1,142 +1,142 @@ /* * Copyright (C) 2005 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 char command = 0; static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output"}, + {"help", 0, 0, '?', "\tThis text"}, + {"version", 0, 0, '$', "\tVersion information" }, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"-spacer-", 0, 0, '-', "\nCommands:"}, - {"now", 0, 0, 'n', "Display the current date/time"}, + {"now", 0, 0, 'n', "\tDisplay the current date/time"}, {"date", 1, 0, 'd', "Parse an ISO8601 date/time. Eg. '2005-01-20 00:30:00 +01:00' or '2005-040'"}, {"period", 1, 0, 'p', "Parse an ISO8601 date/time with interval/period (wth start time). Eg. '2005-040/2005-043'"}, {"duration", 1, 0, 'D', "Parse an ISO8601 date/time with duration (wth start time). Eg. '2005-040/P1M'"}, {"-spacer-",0, 0, '-', "\nOutput Modifiers:"}, - {"local", 0, 0, 'L', "Show result as a 'local' date/time"}, - {"ordinal", 0, 0, 'O', "Show result as an 'ordinal' date/time"}, - {"week", 0, 0, 'W', "Show result as an 'calendar week' date/time"}, + {"local", 0, 0, 'L', "\tShow result as a 'local' date/time"}, + {"ordinal", 0, 0, 'O', "\tShow result as an 'ordinal' date/time"}, + {"week", 0, 0, 'W', "\tShow result as an 'calendar week' date/time"}, {"-spacer-",0, 0, '-', "\nFor more information on the ISO8601 standard, see: http://en.wikipedia.org/wiki/ISO_8601"}, {0, 0, 0, 0} }; int main(int argc, char **argv) { int argerr = 0; int flag; int index = 0; int print_options = 0; char *input_s = NULL; char *mutable_s = NULL; crm_log_init("iso8601", LOG_INFO, FALSE, TRUE, 0, NULL); - crm_set_options("V?d:p:D:WOLn", "{command} [output modifier] ", long_options, "Display and parse ISO8601 dates and times"); + crm_set_options("V?d:p:D:WOLn", "command [output modifier] ", long_options, "Display and parse ISO8601 dates and times"); if(argc < 2) { argerr++; } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch(flag) { case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '?': case '$': crm_help(flag, 0); break; case 'n': command = flag; break; case 'd': case 'p': case 'D': command = flag; input_s = crm_strdup(optarg); break; case 'W': print_options |= ha_date_weeks; break; case 'O': print_options |= ha_date_ordinal; break; case 'L': print_options |= ha_log_local; break; } } if(input_s == NULL && command != 'n') { crm_help('?', 1); } mutable_s = input_s; if(command == 'd') { ha_time_t *date_time = parse_date(&mutable_s); if(date_time == NULL) { fprintf(stderr, "Invalid date/time specified: %s\n", input_s); crm_help('?',1); } log_date(LOG_INFO, "parsed", date_time, print_options|ha_log_date|ha_log_time); } else if(command == 'p') { ha_time_period_t *interval = parse_time_period(&mutable_s); if(interval == NULL) { fprintf(stderr, "Invalid interval specified: %s\n", input_s); crm_help('?',1); } log_time_period(LOG_INFO, interval, print_options|ha_log_date|ha_log_time); } else if(command == 'D') { ha_time_t *duration = parse_time_duration(&mutable_s); if(duration == NULL) { fprintf(stderr, "Invalid duration specified: %s\n", input_s); crm_help('?',1); } log_date(LOG_INFO, "Duration", duration, print_options|ha_log_date|ha_log_time|ha_log_local); } else if(command == 'n') { ha_time_t *now = new_ha_date(TRUE); if(now == NULL) { fprintf(stderr, "Internal error: couldnt determin 'now' !\n"); crm_help('?',1); } log_date(LOG_INFO, "Current date/time", now, print_options|ha_log_date|ha_log_time); } return 0; } diff --git a/tools/xml_diff.c b/tools/xml_diff.c index a3f12b4029..5de9b6f94c 100644 --- a/tools/xml_diff.c +++ b/tools/xml_diff.c @@ -1,206 +1,211 @@ /* * 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 static struct crm_option long_options[] = { /* Top-level Options */ - {"help", 0, 0, '?', "This text"}, - {"version", 0, 0, '$', "Version information" }, - {"verbose", 0, 0, 'V', "Increase debug output\n"}, - - {"original", 1, 0, 'o', }, - {"new", 1, 0, 'n', }, - {"original-string", 1, 0, 'O', }, - {"new-string", 1, 0, 'N'}, - {"stdin", 0, 0, 's'}, - - {"patch", 1, 0, 'p', "Apply a patch to the original XML"}, - {"cib", 0, 0, 'c', "Compare/patch the inputs as a CIB"}, + {"help", 0, 0, '?', "\t\tThis text"}, + {"version", 0, 0, '$', "\t\tVersion information" }, + {"verbose", 0, 0, 'V', "\t\tIncrease debug output\n"}, + + {"-spacer-", 1, 0, '-', "\nOriginal XML:"}, + {"original", 1, 0, 'o', "\tXML is contained in the named file"}, + {"original-string", 1, 0, 'O', "XML is contained in the supplied string"}, + + {"-spacer-", 1, 0, '-', "\nOperation:"}, + {"new", 1, 0, 'n', "\tCompare the original XML to the contents of the named file"}, + {"new-string", 1, 0, 'N', "\tCompare the original XML to the contents of the supplied string"}, + {"patch", 1, 0, 'p', "\tPatch the original XML with the contents of the named file"}, + + {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, + {"cib", 0, 0, 'c', "\t\tCompare/patch the inputs as a CIB (includes versions details)"}, + {"stdin", 0, 0, 's', NULL, 1}, + {0, 0, 0, 0} }; int main(int argc, char **argv) { gboolean apply = FALSE; gboolean raw_1 = FALSE; gboolean raw_2 = FALSE; gboolean filter = FALSE; gboolean use_stdin = FALSE; gboolean as_cib = FALSE; int argerr = 0; int flag; xmlNode *object_1 = NULL; xmlNode *object_2 = NULL; xmlNode *output = NULL; const char *xml_file_1 = NULL; const char *xml_file_2 = NULL; int option_index = 0; crm_log_init("crm_diff", LOG_CRIT-1, FALSE, FALSE, 0, NULL); - crm_set_options("V?$o:n:p:scfO:N:", "[-?Vv] -[oO] -[pnN]", long_options, + crm_set_options("V?$o:n:p:scfO:N:", "original_xml operation [options]", long_options, "Compare and patch xml files\n"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'o': xml_file_1 = optarg; break; case 'O': xml_file_1 = optarg; raw_1 = TRUE; break; case 'n': xml_file_2 = optarg; break; case 'N': xml_file_2 = optarg; raw_2 = TRUE; break; case 'p': xml_file_2 = optarg; apply = TRUE; break; case 'f': filter = TRUE; break; case 's': use_stdin = TRUE; break; case 'c': as_cib = TRUE; break; case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case '?': case '$': crm_help(flag, LSB_EXIT_OK); 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) { crm_help('?', LSB_EXIT_GENERIC); } if(raw_1) { object_1 = string2xml(xml_file_1); } else if(use_stdin) { fprintf(stderr, "Input first XML fragment:"); object_1 = stdin2xml(); } else if(xml_file_1 != NULL) { object_1 = filename2xml(xml_file_1); } if(raw_2) { object_2 = string2xml(xml_file_2); } else if(use_stdin) { fprintf(stderr, "Input second XML fragment:"); object_2 = stdin2xml(); } else if(xml_file_2 != NULL) { object_2 = filename2xml(xml_file_2); } if(object_1 == NULL) { fprintf(stderr, "Could not parse the first XML fragment"); return 1; } if(object_2 == NULL) { fprintf(stderr, "Could not parse the second XML fragment"); return 1; } if(apply) { if(as_cib == FALSE) { apply_xml_diff(object_1, object_2, &output); } else { apply_cib_diff(object_1, object_2, &output); } } else { if(as_cib == FALSE) { output = diff_xml_object(object_1, object_2, filter); } else { output = diff_cib_object(object_1, object_2, filter); } } if(output != NULL) { char *buffer = dump_xml_formatted(output); fprintf(stdout, "%s", crm_str(buffer)); crm_free(buffer); } free_xml(object_1); free_xml(object_2); free_xml(output); if(apply == FALSE && output != NULL) { return 1; } return 0; }