diff --git a/fencing/fence_dummy b/fencing/fence_dummy index e9e7f6c423..70283a0e06 100755 --- a/fencing/fence_dummy +++ b/fencing/fence_dummy @@ -1,388 +1,391 @@ #!/usr/bin/python """Dummy fence agent for testing """ # Pacemaker targets compatibility with Python 2.6+ and 3.2+ from __future__ import print_function, unicode_literals, absolute_import, division __copyright__ = "Copyright (C) 2012-2016 Andrew Beekhof " __license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY" import io import os import re import sys import time import random import atexit import getopt AGENT_VERSION = "4.0.0" OCF_VERSION = "1.0" SHORT_DESC = "Dummy fence agent" LONG_DESC = """fence_dummy is a fake fencing agent which reports success based on its mode (pass|fail|random) without doing anything.""" all_opt = { - "help" : { - "getopt" : "h", - "longopt" : "help", - "help" : "-h, --help Display this help and exit", - "required" : "0", - "shortdesc" : "Display help and exit", - "order" : 54 }, - "version" : { - "getopt" : "V", - "longopt" : "version", - "help" : "-V, --version Display version information and exit", - "required" : "0", - "shortdesc" : "Display version information and exit", - "order" : 53 }, "quiet" : { "getopt" : "q", "help" : "", "order" : 50 }, "verbose" : { "getopt" : "v", "longopt" : "verbose", "help" : "-v, --verbose Verbose mode", "required" : "0", "shortdesc" : "Verbose mode", "order" : 51 }, "debug" : { "getopt" : "D:", "longopt" : "debug-file", "help" : "-D, --debug-file=[debugfile] Debugging to output file", "required" : "0", "shortdesc" : "Write debug information to given file", "order" : 52 }, - "random_sleep_range": { - "getopt" : "R:", + "version" : { + "getopt" : "V", + "longopt" : "version", + "help" : "-V, --version Display version information and exit", "required" : "0", - "longopt" : "random_sleep_range", - "help" : "--random_sleep_range=[seconds] Sleep between 1 and [seconds] before returning", - "shortdesc" : "Issue a sleep between 1 and [seconds]", + "shortdesc" : "Display version information and exit", + "order" : 53 }, + "help" : { + "getopt" : "h", + "longopt" : "help", + "help" : "-h, --help Display this help and exit", + "required" : "0", + "shortdesc" : "Display help and exit", + "order" : 54 }, + "action" : { + "getopt" : "o:", + "longopt" : "action", + "help" : "-o, --action=[action] Action: status, list, reboot (default), off or on", + "required" : "1", + "shortdesc" : "Fencing Action", + "default" : "reboot", "order" : 1 }, + "nodename" : { + "getopt" : "N:", + "longopt" : "nodename", + "help" : "-N, --nodename Node name of fence victim (ignored)", + "required" : "0", + "shortdesc" : "The node name of fence victim (ignored)", + "order" : 2 }, "mode": { "getopt" : "M:", "longopt" : "mode", "required" : "0", - "help" : "--mode=(pass|fail|random) What exit status should be returned for this operation", - "shortdesc" : "Should operations always pass, always fail or fail at random", - "order" : 1 }, + "help" : "-M, --mode=(pass|fail|random) Exit status to return for non-monitor operations", + "shortdesc" : "Whether fence operations should always pass, always fail, or fail at random", + "order" : 3 }, + "random_sleep_range": { + "getopt" : "R:", + "required" : "0", + "longopt" : "random_sleep_range", + "help" : "-R, --random_sleep_range=[seconds] Sleep between 1 and [seconds] before returning", + "shortdesc" : "Wait randomly between 1 and [seconds]", + "order" : 3 }, + "mock_dynamic_hosts" : { + "getopt" : "H:", + "longopt" : "mock_dynamic_hosts", + "help" : "-H, --mock_dynamic_hosts=[list] What to return when dynamically queried for possible targets", + "required" : "0", + "shortdesc" : "A list of hosts we can fence", + "order" : 3 }, "delay" : { "getopt" : "f:", "longopt" : "delay", - "help" : "--delay [seconds] Wait X seconds before fencing is started", + "help" : "-f, --delay [seconds] Wait X seconds before fencing is started", "required" : "0", "shortdesc" : "Wait X seconds before fencing is started", "default" : "0", - "order" : 200 }, - "action" : { - "getopt" : "o:", - "longopt" : "action", - "help" : "-o, --action=[action] Action: status, list, reboot (default), off or on", - "required" : "1", - "shortdesc" : "Fencing Action", - "default" : "reboot", - "order" : 1 }, + "order" : 3 }, "port" : { "getopt" : "n:", "longopt" : "plug", - "help" : "-n, --plug=[id] Physical plug number on device", + "help" : "-n, --plug=[id] Physical plug number on device (ignored)", "required" : "1", - "shortdesc" : "Physical plug number or name of virtual machine", - "order" : 1 }, + "shortdesc" : "Ignored", + "order" : 4 }, "switch" : { "getopt" : "s:", "longopt" : "switch", - "help" : "-s, --switch=[id] Physical switch number on device", - "required" : "0", - "shortdesc" : "Physical switch number on device", - "order" : 1 }, - "nodename" : { - "getopt" : "N:", - "longopt" : "nodename", - "help" : "-N, --nodename Node name of fence victim", + "help" : "-s, --switch=[id] Physical switch number on device (ignored)", "required" : "0", - "shortdesc" : "The node name of fence victim", - "order" : 1}, + "shortdesc" : "Ignored", + "order" : 4 }, "nodeid" : { "getopt" : "i:", "longopt" : "nodeid", - "help" : "-i, --nodeid Corosync id of fence victim", + "help" : "-i, --nodeid Corosync id of fence victim (ignored)", "required" : "0", - "shortdesc" : "The corosync id of fence victim", - "order" : 1}, + "shortdesc" : "Ignored", + "order" : 4 }, "uuid" : { "getopt" : "U:", "longopt" : "uuid", - "help" : "-U, --uuid UUID of the VM to fence", + "help" : "-U, --uuid UUID of the VM to fence (ignored)", "required" : "0", - "shortdesc" : "The UUID of the virtual machine to fence", - "order" : 1}, - "mock_dynamic_hosts" : { - "getopt" : "H:", - "longopt" : "mock_dynamic_hosts", - "help" : "-H, --mock_dynamic_hosts=[list] What to return when dynamically queried for possible targets", - "required" : "0", - "shortdesc" : "A list of hosts we can fence.", - "order" : 1} + "shortdesc" : "Ignored", + "order" : 4 } } def fail_usage(message): sys.stderr.write("%s\nPlease use '-h' for usage\n" % message) sys.exit(1) def show_docs(options): """ Handle informational options (display info and exit). """ device_opt = options["device_opt"] if "-h" in options: usage(device_opt) sys.exit(0) if "-o" in options and options["-o"].lower() == "metadata": metadata(device_opt, options) sys.exit(0) if "-V" in options: print(AGENT_VERSION) sys.exit(0) def usage(avail_opt): global all_opt print("Usage:") print("\t" + os.path.basename(sys.argv[0]) + " [options]") print("Options:") sorted_list = [ (key, all_opt[key]) for key in avail_opt ] sorted_list.sort(key=lambda x: x[1]["order"]) for key, value in sorted_list: if len(value["help"]) != 0: print(" " + value["help"]) def metadata(avail_opt, options): global all_opt # This log is just for testing handling of stderr output sys.stderr.write("asked for fence_dummy metadata\n") sorted_list = [ (key, all_opt[key]) for key in avail_opt ] sorted_list.sort(key=lambda x: x[1]["order"]) print(""" %s %s """ % (os.path.basename(sys.argv[0]), SHORT_DESC, AGENT_VERSION, OCF_VERSION, LONG_DESC)) for option, value in sorted_list: if "shortdesc" in all_opt[option]: print("\t") default = "" if "default" in all_opt[option]: default = "default=\""+str(all_opt[option]["default"])+"\"" elif ("-" + all_opt[option]["getopt"][:-1]) in options: if options["-" + all_opt[option]["getopt"][:-1]]: try: default = "default=\"" + options["-" + all_opt[option]["getopt"][:-1]] + "\"" except TypeError: ## @todo/@note: Currently there is no clean way how to handle lists ## we can create a string from it but we can't set it on command line default = "default=\"" + str(options["-" + all_opt[option]["getopt"][:-1]]) +"\"" elif ("-" + all_opt[option]["getopt"]) in options: default = "default=\"true\" " mixed = all_opt[option]["help"] ## split it between option and help text res = re.compile("^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed) if (None != res): mixed = res.group(1) mixed = mixed.replace("<", "<").replace(">", ">") print("\t\t") if all_opt[option]["getopt"].count(":") > 0: print("\t\t") else: print("\t\t") print("\t\t" + all_opt[option]["shortdesc"] + "") print("\t") print(""" \t \t \t \t \t \t \t """) + def process_input(avail_opt): global all_opt ## ## Set standard environment ##### os.putenv("LANG", "C") os.putenv("LC_ALL", "C") ## ## Prepare list of options for getopt ##### getopt_string = "" longopt_list = [ ] for k in avail_opt: if k in all_opt: getopt_string += all_opt[k]["getopt"] else: fail_usage("Parse error: unknown option '"+k+"'") if k in all_opt and "longopt" in all_opt[k]: if all_opt[k]["getopt"].endswith(":"): longopt_list.append(all_opt[k]["longopt"] + "=") else: longopt_list.append(all_opt[k]["longopt"]) ## ## Read options from command line or standard input ##### if len(sys.argv) > 1: try: opt, args = getopt.gnu_getopt(sys.argv[1:], getopt_string, longopt_list) except getopt.GetoptError as error: fail_usage("Parse error: " + error.msg) ## Transform longopt to short one which are used in fencing agents ##### old_opt = opt opt = { } for o in dict(old_opt).keys(): if o.startswith("--"): for x in all_opt.keys(): if "longopt" in all_opt[x] and "--" + all_opt[x]["longopt"] == o: opt["-" + all_opt[x]["getopt"].rstrip(":")] = dict(old_opt)[o] else: opt[o] = dict(old_opt)[o] ## Compatibility Layer ##### z = dict(opt) if "-T" in z: z["-o"] = "status" if "-n" in z: z["-m"] = z["-n"] opt = z ## ##### else: opt = { } name = "" for line in sys.stdin.readlines(): line = line.strip() if ((line.startswith("#")) or (len(line) == 0)): continue (name, value) = (line + "=").split("=", 1) value = value[:-1] ## Compatibility Layer ###### if name == "option": name = "action" ## ###### if name not in avail_opt: sys.stderr.write("Parse error: Ignoring unknown option '"+line+"'\n") continue if all_opt[name]["getopt"].endswith(":"): opt["-"+all_opt[name]["getopt"].rstrip(":")] = value elif ((value == "1") or (value.lower() == "yes") or (value.lower() == "on") or (value.lower() == "true")): opt["-"+all_opt[name]["getopt"]] = "1" return opt + def atexit_handler(): try: sys.stdout.close() os.close(1) except IOError: sys.stderr.write("%s failed to close standard output\n"%(sys.argv[0])) sys.exit(1) + def main(): global all_opt device_opt = all_opt.keys() ## Defaults for fence agent atexit.register(atexit_handler) options = process_input(device_opt) options["device_opt"] = device_opt show_docs(options) # dump input to file if "-D" in options: try: f = io.open(options["-D"], 'at') f.write("### %s ###\n" % (time.strftime("%Y-%m-%d %H:%M:%S"))) for v in sorted(options): f.write("%s=%s\n" % (v, options[v])) f.write("###\n") f.close() except IOError: pass if "-f" in options: val = int(options["-f"]) sys.stderr.write("delay sleep for %d seconds\n" % val) time.sleep(val) # random sleep for testing if "-R" in options: val = int(options["-R"]) ran = random.randint(1, val) sys.stderr.write("random sleep for %d seconds\n" % ran) time.sleep(ran) if "-o" in options and (options["-o"] == "monitor"): sys.stderr.write("fence_dummy monitor called\n") sys.exit(0) if "-o" in options and (options["-o"] == "list"): sys.stderr.write("fence_dummy action (list) called\n") if "-H" in options: print(options["-H"]) else: sys.stderr.write("were asked for hostlist but attribute mock_dynamic_hosts wasn't set\n") exitcode = random.randint(0, 1) if "-M" in options: if options["-M"] == "pass": exitcode = 0 elif options["-M"] == "fail": exitcode = 1 # Ensure we generate some error output on failure exit. if exitcode == 1: sys.stderr.write("simulated fencing failure\n") sys.exit(exitcode) if __name__ == "__main__": main()