diff --git a/fencing/admin.c b/fencing/admin.c index 4bf0ca9df8..52780b4a58 100644 --- a/fencing/admin.c +++ b/fencing/admin.c @@ -1,513 +1,527 @@ -/* +/* * Copyright (C) 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* *INDENT-OFF* */ static struct crm_option long_options[] = { {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"verbose", 0, 0, 'V', "\tIncrease debug output"}, {"quiet", 0, 0, 'q', "\tPrint only essential output"}, {"-spacer-", 0, 0, '-', "Commands:"}, {"list", 1, 0, 'l', "List devices that can terminate the specified host"}, {"list-registered", 0, 0, 'L', "List all registered devices"}, {"list-installed", 0, 0, 'I', "List all installed devices"}, {"-spacer-", 0, 0, '-', ""}, {"metadata", 0, 0, 'M', "Check the device's metadata"}, {"query", 1, 0, 'Q', "Check the device's status"}, {"fence", 1, 0, 'F', "Fence the named host"}, {"unfence", 1, 0, 'U', "Unfence the named host"}, {"reboot", 1, 0, 'B', "Reboot the named host"}, {"confirm", 1, 0, 'C', "Confirm the named host is now safely down"}, {"history", 1, 0, 'H', "Retrieve last fencing operation"}, {"-spacer-", 0, 0, '-', ""}, {"register", 1, 0, 'R', "Register the named stonith device. Requires: --agent, optional: --option"}, {"deregister", 1, 0, 'D', "De-register the named stonith device"}, {"register-level", 1, 0, 'r', "Register a stonith level for the named host. Requires: --index, one or more --device entries"}, {"deregister-level", 1, 0, 'd', "De-register a stonith level for the named host. Requires: --index"}, {"-spacer-", 0, 0, '-', ""}, {"-spacer-", 0, 0, '-', "Options and modifiers:"}, {"agent", 1, 0, 'a', "The agent (eg. fence_xvm) to instantiate when calling with --register"}, {"env-option", 1, 0, 'e'}, {"option", 1, 0, 'o'}, + {"tag", 1, 0, 'T', "Identify fencing operations with the specified tag.\n\t" + "Useful when there are multiple entities that might be invoking stonith_admin(8)"}, {"device", 1, 0, 'v', "A device to associate with a given host and stonith level"}, {"index", 1, 0, 'i', "The stonith level (1-9)"}, {"timeout", 1, 0, 't', "Operation timeout in seconds"}, {"tolerance", 1, 0, 0, "(Advanced) Do nothing if an equivalent --fence request succeeded less than N seconds earlier" }, {"list-all", 0, 0, 'L', "legacy alias for --list-registered"}, {0, 0, 0, 0} }; /* *INDENT-ON* */ int st_opts = st_opt_sync_call | st_opt_allow_suicide; GMainLoop *mainloop = NULL; struct { stonith_t *st; const char *target; const char *action; + char *name; int timeout; int tolerance; int rc; } async_fence_data; static int try_mainloop_connect(void) { stonith_t *st = async_fence_data.st; int tries = 10; int i = 0; int rc = 0; for (i = 0; i < tries; i++) { - rc = st->cmds->connect(st, crm_system_name, NULL); + crm_debug("Connecting as %s", async_fence_data.name); + rc = st->cmds->connect(st, async_fence_data.name, NULL); if (!rc) { crm_debug("stonith client connection established"); return 0; } else { crm_debug("stonith client connection failed"); } sleep(1); } crm_err("Couldn not connect to stonithd. \n"); return -1; } static void notify_callback(stonith_t * st, stonith_event_t * e) { if (e->result != pcmk_ok) { return; } if (safe_str_eq(async_fence_data.target, e->target) && safe_str_eq(async_fence_data.action, e->action)) { async_fence_data.rc = e->result; g_main_loop_quit(mainloop); } } static void fence_callback(stonith_t * stonith, stonith_callback_data_t * data) { async_fence_data.rc = data->rc; g_main_loop_quit(mainloop); } static gboolean async_fence_helper(gpointer user_data) { stonith_t *st = async_fence_data.st; int call_id = 0; if (try_mainloop_connect()) { g_main_loop_quit(mainloop); return TRUE; } st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, notify_callback); call_id = st->cmds->fence(st, st_opt_allow_suicide, async_fence_data.target, async_fence_data.action, async_fence_data.timeout, async_fence_data.tolerance); if (call_id < 0) { g_main_loop_quit(mainloop); return TRUE; } st->cmds->register_callback(st, call_id, async_fence_data.timeout, st_opt_timeout_updates, NULL, "callback", fence_callback); return TRUE; } static int mainloop_fencing(stonith_t * st, const char *target, const char *action, int timeout, int tolerance) { crm_trigger_t *trig; async_fence_data.st = st; async_fence_data.target = target; async_fence_data.action = action; async_fence_data.timeout = timeout; async_fence_data.tolerance = tolerance; async_fence_data.rc = -1; trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL); mainloop_set_trigger(trig); mainloop = g_main_new(FALSE); g_main_run(mainloop); return async_fence_data.rc; } int main(int argc, char **argv) { int flag; int rc = 0; int quiet = 0; int verbose = 0; int argerr = 0; int timeout = 120; int option_index = 0; int fence_level = 0; int no_connect = 0; int tolerance = 0; char name[512]; char value[512]; const char *agent = NULL; const char *device = NULL; const char *target = NULL; const char *longname = NULL; char action = 0; stonith_t *st = NULL; stonith_key_value_t *params = NULL; stonith_key_value_t *devices = NULL; stonith_key_value_t *dIter = NULL; crm_log_cli_init("stonith_admin"); crm_set_options(NULL, "mode [options]", long_options, "Provides access to the stonith-ng API.\n" "\nAllows the administrator to add/remove/list devices, check device and host status and fence hosts\n"); + async_fence_data.name = strdup(crm_system_name); + while (1) { flag = crm_get_option_long(argc, argv, &option_index, &longname); if (flag == -1) break; switch (flag) { case 'V': verbose = 1; crm_bump_log_level(argc, argv); break; case '$': case '?': crm_help(flag, EX_OK); break; case 'I': no_connect = 1; /* fall through */ case 'L': action = flag; break; case 'q': quiet = 1; break; case 'Q': case 'R': case 'D': action = flag; device = optarg; break; + case 'T': + free(async_fence_data.name); + async_fence_data.name = g_strdup_printf("%s.%s", crm_system_name, optarg); + break; case 'a': agent = optarg; break; case 'l': target = optarg; action = 'L'; break; case 'M': no_connect = 1; action = flag; break; case 't': timeout = crm_atoi(optarg, NULL); break; case 'B': case 'F': case 'U': /* using mainloop here */ no_connect = 1; /* fall through */ case 'C': /* Always log the input arguments */ crm_log_args(argc, argv); target = optarg; action = flag; break; case 'H': case 'r': case 'd': target = optarg; action = flag; break; case 'i': fence_level = crm_atoi(optarg, NULL); break; case 'v': devices = stonith_key_value_add(devices, NULL, optarg); break; case 'o': crm_info("Scanning: -o %s", optarg); rc = sscanf(optarg, "%[^=]=%[^=]", name, value); if (rc != 2) { crm_err("Invalid option: -o %s", optarg); ++argerr; } else { crm_info("Got: '%s'='%s'", name, value); params = stonith_key_value_add(params, name, value); } break; case 'e': { char *key = crm_concat("OCF_RESKEY", optarg, '_'); const char *env = getenv(key); if (env == NULL) { crm_err("Invalid option: -e %s", optarg); ++argerr; } else { crm_info("Got: '%s'='%s'", optarg, env); params = stonith_key_value_add(params, optarg, env); } } break; case 0: if (safe_str_eq("tolerance", longname)) { tolerance = crm_get_msec(optarg) / 1000; /* Send in seconds */ } break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', EX_USAGE); } crm_debug("Create"); st = stonith_api_new(); + crm_debug("Created"); if (!no_connect) { - rc = st->cmds->connect(st, crm_system_name, NULL); + crm_debug("Connecting as %s", async_fence_data.name); + rc = st->cmds->connect(st, async_fence_data.name, NULL); + crm_debug("Connect: %d", rc); if (rc < 0) { goto done; } } switch (action) { case 'I': rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout); for (dIter = devices; dIter; dIter = dIter->next) { fprintf(stdout, " %s\n", dIter->value); } if (rc == 0) { fprintf(stderr, "No devices found\n"); } else if (rc > 0) { fprintf(stderr, "%d devices found\n", rc); rc = 0; } stonith_key_value_freeall(devices, 1, 1); break; case 'L': rc = st->cmds->query(st, st_opts, target, &devices, timeout); for (dIter = devices; dIter; dIter = dIter->next) { fprintf(stdout, " %s\n", dIter->value); } if (rc == 0) { fprintf(stderr, "No devices found\n"); } else if (rc > 0) { fprintf(stderr, "%d devices found\n", rc); rc = 0; } stonith_key_value_freeall(devices, 1, 1); break; case 'Q': rc = st->cmds->monitor(st, st_opts, device, timeout); if (rc < 0) { rc = st->cmds->list(st, st_opts, device, NULL, timeout); } break; case 'R': rc = st->cmds->register_device(st, st_opts, device, "stonith-ng", agent, params); break; case 'D': rc = st->cmds->remove_device(st, st_opts, device); break; case 'r': rc = st->cmds->register_level(st, st_opts, target, fence_level, devices); break; case 'd': rc = st->cmds->remove_level(st, st_opts, target, fence_level); break; case 'M': if (agent == NULL) { printf("Please specify an agent to query using -a,--agent [value]\n"); return -1; } else { char *buffer = NULL; rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer, timeout); if (rc == pcmk_ok) { printf("%s\n", buffer); } free(buffer); } break; case 'C': rc = st->cmds->confirm(st, st_opts, target); break; case 'B': rc = mainloop_fencing(st, target, "reboot", timeout, tolerance); break; case 'F': rc = mainloop_fencing(st, target, "off", timeout, tolerance); break; case 'U': rc = mainloop_fencing(st, target, "on", timeout, tolerance); break; case 'H': { stonith_history_t *history, *hp, *latest = NULL; rc = st->cmds->history(st, st_opts, target, &history, timeout); for (hp = history; hp; hp = hp->next) { char *action_s = NULL; time_t complete = hp->completed; if (hp->state == st_done) { latest = hp; } if (quiet || !verbose) { continue; } else if (hp->action == NULL) { action_s = strdup("unknown"); } else if (hp->action[0] != 'r') { action_s = crm_concat("turn", hp->action, ' '); } else { action_s = strdup(hp->action); } if (hp->state == st_failed) { printf("%s failed to %s node %s on behalf of %s at %s\n", hp->delegate ? hp->delegate : "We", action_s, hp->target, hp->origin, ctime(&complete)); } else if (hp->state == st_done && hp->delegate) { printf("%s was able to %s node %s on behalf of %s at %s\n", hp->delegate, action_s, hp->target, hp->origin, ctime(&complete)); } else if (hp->state == st_done) { printf("We were able to %s node %s on behalf of %s at %s\n", action_s, hp->target, hp->origin, ctime(&complete)); } else { printf("%s wishes to %s node %s - %d %d\n", hp->origin, action_s, hp->target, hp->state, hp->completed); } free(action_s); } if (latest) { if (quiet) { printf("%d\n", latest->completed); } else { char *action_s = NULL; time_t complete = latest->completed; if (latest->action == NULL) { action_s = strdup("unknown"); } else if (latest->action[0] != 'r') { action_s = crm_concat("turn", latest->action, ' '); } else { action_s = strdup(latest->action); } printf("%s was able to %s node %s on behalf of %s at %s\n", latest->delegate ? latest->delegate : "We", action_s, latest->target, latest->origin, ctime(&complete)); free(action_s); } } break; } /* closing bracket for -H case */ } /* closing bracket for switch case */ done: + free(async_fence_data.name); crm_info("Command returned: %s (%d)", pcmk_strerror(rc), rc); if (rc < 0) { printf("Command failed: %s\n", pcmk_strerror(rc)); } stonith_key_value_freeall(params, 1, 1); st->cmds->disconnect(st); crm_debug("Disconnect: %d", rc); crm_debug("Destroy"); stonith_api_delete(st); return rc; } diff --git a/fencing/fence_pcmk b/fencing/fence_pcmk index 9baec5c287..51c56467c2 100755 --- a/fencing/fence_pcmk +++ b/fencing/fence_pcmk @@ -1,223 +1,223 @@ #!/usr/bin/perl use Getopt::Long; my $ME = $0; END { defined fileno STDOUT or return; close STDOUT and return; warn "$ME: failed to close standard output: $!\n"; $? ||= 1; } # Get the program name from $0 and strip directory names $_=$0; s/.*\///; my $pname = $_; $opt_o = 'reset'; # Default fence action $opt_t = '300'; # Default fence timeout (seconds) $extra_args = '-E'; sub usage { print "Helper that presents a RHCS-style interface to stonith-ng for CMAN based clusters\n\n"; print "Should never need to use invoked by the user directly\n\n"; print "\n"; print "Usage: $pname [options]\n"; print "\n"; print "Options:\n"; print " -h usage\n"; print " -n nodename\n"; print " -o Action: on | off | reset (default) | stat | hostlist\n"; print " -q quiet mode\n"; print " -V version\n"; exit 0; } sub print_metadata { print ' Should never need to use invoked by the user directly and should only be configured in cluster.conf, not directly in Pacemaker. http://www.clusterlabs.org Fencing Action Physical plug number or name of virtual machine Display help and exit '; } sub fail { ($msg) = @_; print $msg."\n" unless defined $opt_q; $t->close if defined $t; exit 1; } sub fail_usage { ($msg)=@_; print STDERR $msg."\n" if $msg; print STDERR "Please use '-h' for usage.\n"; exit 1; } sub version { print "1.0.0\n"; exit 0; } sub get_options_stdin { my $opt; my $line = 0; while( defined($in = <>) ) { $_ = $in; chomp; # strip leading and trailing whitespace s/^\s*//; s/\s*$//; # skip comments next if /^#/; $line+=1; $opt=$_; next unless $opt; ($name,$val)=split /\s*=\s*/, $opt, 2; if ( $name eq "" ) { print STDERR "parse error: illegal name in option $line\n"; exit 2; } # DO NOTHING -- this field is used by fenced elsif ($name eq "agent" ) {} elsif ($name eq "timeout" ) { $opt_t = $val; } elsif ($name eq "option" || $name eq "action" ) { $opt_o = $val; } elsif ($name eq "port" ) { $opt_n = $val; } else { $ENV{$name} = $val; } } } ######################################################################33 # MAIN if (@ARGV > 0) { GetOptions("n=s"=>\$opt_n, "o=s"=>\$opt_o, "t=s"=>\$opt_t, "q" =>\$opt_q, "V" =>\$opt_V, "version" =>\$opt_V, "help" =>\$opt_h, "h" =>\$opt_h) || fail_usage; foreach (@ARGV) { print "$_\n"; } # getopts("ht:n:o:s:qV") || fail_usage ; usage if defined $opt_h; version if defined $opt_V; fail_usage "Unknown parameter." if (@ARGV > 0); } else { get_options_stdin(); } if ((defined $opt_o) && ($opt_o =~ /metadata/i)) { print_metadata(); exit 0; } $opt_o=lc($opt_o); fail "failed: unrecognised action: $opt_o" unless $opt_o =~ /^(on|off|reset|reboot|stat|status|monitor|list|hostlist|poweroff|poweron)$/; sub term_handler { local $SIG{TERM} = 'IGNORE'; kill(TERM,-getpgrp($$)); } setpgrp($$,0); $agent_pid=$$; if ( $pid=fork() == 0 ) { $cmd="--help $opt_o"; if ( $opt_o eq "monitor" || $opt_o eq "stat" || $opt_o eq "status" || $opt_o eq "list" ) { $cmd="--list"; $cmd="--list-registered" unless defined $opt_n; } else { fail "failed: no plug number" unless defined $opt_n; if ( $opt_o eq "reboot" || $opt_o eq "reset" ) { $cmd="--reboot"; } elsif ( $opt_o eq "on" || $opt_o eq "poweron" ) { $cmd="--unfence"; } elsif ( $opt_o eq "off" || $opt_o eq "poweroff" ) { $cmd="--fence"; } system "logger -p daemon.notice -t \"fence_pcmk[$agent_pid]\" \"Requesting Pacemaker fence $opt_n ($opt_o)\""; } - exec "stonith_admin $cmd $opt_n --tolerance 5s" or die "failed to exec \"stonith_admin $cmd $opt_n\"\n"; + exec "stonith_admin $cmd $opt_n --tolerance 5s --tag cman" or die "failed to exec \"stonith_admin $cmd $opt_n\"\n"; } $SIG{TERM} = \&term_handler; wait; $status=$?/256; print (($status == 0 ? "success":"failed") . ": $opt_n $status\n") unless defined $opt_q; if ( $status != 0 ) { system "logger -p daemon.notice -t \"fence_pcmk[$agent_pid]\" \"Call to fence $opt_n ($opt_o) failed with rc=$status\""; } exit ($status == 0 ? 0 : 1 );