diff options
Diffstat (limited to 'agents/stonith/fence_watchdog.in')
-rwxr-xr-x | agents/stonith/fence_watchdog.in | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/agents/stonith/fence_watchdog.in b/agents/stonith/fence_watchdog.in new file mode 100755 index 0000000..f43ab87 --- /dev/null +++ b/agents/stonith/fence_watchdog.in @@ -0,0 +1,284 @@ +#!@PYTHON@ +"""Dummy watchdog fence agent for providing meta-data for the pacemaker internal agent +""" + +__copyright__ = "Copyright 2012-2022 the Pacemaker project contributors" +__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY" + +import io +import os +import re +import sys +import atexit +import getopt + +AGENT_VERSION = "1.0.0" +SHORT_DESC = "Dummy watchdog fence agent" +LONG_DESC = """fence_watchdog just provides +meta-data - actual fencing is done by the pacemaker internal watchdog agent.""" + +ALL_OPT = { + "version" : { + "getopt" : "V", + "longopt" : "version", + "help" : "-V, --version Display version information and exit", + "required" : "0", + "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: metadata", + "required" : "1", + "shortdesc" : "Fencing Action", + "default" : "metadata", + "order" : 1 + }, + "nodename" : { + "getopt" : "N:", + "longopt" : "nodename", + "help" : "-N, --nodename Node name of fence target (ignored)", + "required" : "0", + "shortdesc" : "Ignored", + "order" : 2 + }, + "plug" : { + "getopt" : "n:", + "longopt" : "plug", + "help" : "-n, --plug=[id] Physical plug number on device (ignored)", + "required" : "1", + "shortdesc" : "Ignored", + "order" : 4 + } +} + + +def agent(): + """ Return name this file was run as. """ + + return os.path.basename(sys.argv[0]) + + +def fail_usage(message): + """ Print a usage message and exit. """ + + sys.exit("%s\nPlease use '-h' for usage" % message) + + +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 sorted_options(avail_opt): + """ Return a list of all options, in their internally specified order. """ + + sorted_list = [(key, ALL_OPT[key]) for key in avail_opt] + sorted_list.sort(key=lambda x: x[1]["order"]) + return sorted_list + + +def usage(avail_opt): + """ Print a usage message. """ + print(LONG_DESC) + print() + print("Usage:") + print("\t" + agent() + " [options]") + print("Options:") + + for dummy, value in sorted_options(avail_opt): + if len(value["help"]) != 0: + print(" " + value["help"]) + + +def metadata(avail_opt, options): + """ Print agent metadata. """ + + print("""<?xml version="1.0" ?> +<resource-agent name="%s" shortdesc="%s"> +<longdesc>%s</longdesc> +<parameters>""" % (agent(), SHORT_DESC, LONG_DESC)) + + for option, dummy in sorted_options(avail_opt): + if "shortdesc" in ALL_OPT[option]: + print(' <parameter name="' + option + + '" required="' + ALL_OPT[option]["required"] + '">') + + default = "" + default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1] + default_name_no_arg = "-" + ALL_OPT[option]["getopt"] + + if "default" in ALL_OPT[option]: + default = 'default="%s"' % str(ALL_OPT[option]["default"]) + elif default_name_arg in options: + if options[default_name_arg]: + try: + default = 'default="%s"' % options[default_name_arg] + 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="%s"' % str(options[default_name_arg]) + elif default_name_no_arg in options: + default = 'default="true"' + + mixed = ALL_OPT[option]["help"] + ## split it between option and help text + res = re.compile(r"^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed) + if None != res: + mixed = res.group(1) + mixed = mixed.replace("<", "<").replace(">", ">") + print(' <getopt mixed="' + mixed + '" />') + + if ALL_OPT[option]["getopt"].count(":") > 0: + print(' <content type="string" ' + default + ' />') + else: + print(' <content type="boolean" ' + default + ' />') + + print(' <shortdesc lang="en">' + ALL_OPT[option]["shortdesc"] + '</shortdesc>') + print(' </parameter>') + + print(' </parameters>\n <actions>') + print(' <action name="on" />') + print(' <action name="off" />') + print(' <action name="reboot" />') + print(' <action name="monitor" />') + print(' <action name="list" />') + print(' <action name="metadata" />') + print(' </actions>') + print('</resource-agent>') + + +def option_longopt(option): + """ Return the getopt-compatible long-option name of the given option. """ + + if ALL_OPT[option]["getopt"].endswith(":"): + return ALL_OPT[option]["longopt"] + "=" + else: + return ALL_OPT[option]["longopt"] + + +def opts_from_command_line(argv, avail_opt): + """ Read options from command-line arguments. """ + + # 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]: + longopt_list.append(option_longopt(k)) + + try: + opt, dummy = getopt.gnu_getopt(argv, 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 old_option in dict(old_opt).keys(): + if old_option.startswith("--"): + for option in ALL_OPT.keys(): + if "longopt" in ALL_OPT[option] and "--" + ALL_OPT[option]["longopt"] == old_option: + opt["-" + ALL_OPT[option]["getopt"].rstrip(":")] = dict(old_opt)[old_option] + else: + opt[old_option] = dict(old_opt)[old_option] + + return opt + + +def opts_from_stdin(avail_opt): + """ Read options from standard input. """ + + 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] + + if name not in avail_opt: + print("Parse error: Ignoring unknown option '%s'" % line, + file=sys.stderr) + continue + + if ALL_OPT[name]["getopt"].endswith(":"): + opt["-"+ALL_OPT[name]["getopt"].rstrip(":")] = value + elif value.lower() in ["1", "yes", "on", "true"]: + opt["-"+ALL_OPT[name]["getopt"]] = "1" + + return opt + + +def process_input(avail_opt): + """ Set standard environment variables, and parse all options. """ + + # Set standard environment + os.putenv("LANG", "C") + os.putenv("LC_ALL", "C") + + # Read options from command line or standard input + if len(sys.argv) > 1: + return opts_from_command_line(sys.argv[1:], avail_opt) + else: + return opts_from_stdin(avail_opt) + + +def atexit_handler(): + """ Close stdout on exit. """ + + try: + sys.stdout.close() + os.close(1) + except IOError: + sys.exit("%s failed to close standard output" % agent()) + + +def main(): + """ Make it so! """ + + 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) + + print("Watchdog fencing may be initiated only by the cluster, not this agent.", + file=sys.stderr) + + sys.exit(1) + + +if __name__ == "__main__": + main() |