fence-agents/backport-fence_eaton_ssh-new-fence-agent-for-Eaton-ePDU-G3-over-ssh.patch

715 lines
28 KiB
Diff
Raw Normal View History

From 849861d2bdf76f70c7ee0b97387a27082f8a3fdd Mon Sep 17 00:00:00 2001
From: Enno G <matrixfueller@gmail.com>
Date: Mon, 26 Jun 2023 11:05:59 +0200
Subject: [PATCH 16/46] fence_eaton_ssh: new fence agent for Eaton ePDU G3 over
SSH (#549)
* fence_eaton_ssh: Initial add
* Docker: Add dockerized build environment
* Fix incorrect repository path in configure.ac
---
README.md | 19 +-
agents/eaton_ssh/fence_eaton_ssh.py | 318 ++++++++++++++++++++++++
configure.ac | 2 +-
docker/Dockerfile | 34 +++
docker/README.md | 10 +
docker/entrypoint.sh | 8 +
fence-agents.spec.in | 13 +
tests/data/metadata/fence_eaton_ssh.xml | 206 +++++++++++++++
8 files changed, 603 insertions(+), 7 deletions(-)
create mode 100644 agents/eaton_ssh/fence_eaton_ssh.py
create mode 100644 docker/Dockerfile
create mode 100644 docker/README.md
create mode 100755 docker/entrypoint.sh
create mode 100644 tests/data/metadata/fence_eaton_ssh.xml
diff --git a/README.md b/README.md
index d9fcb94b..0f3ebbde 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,25 @@
# Fence agents
-Fence agents were developed as device "drivers" which are able to prevent computers from destroying data on shared storage. Their aim is to isolate a corrupted computer, using one of three methods:
+Fence agents were developed as device "drivers" which are able to prevent computers from destroying data on shared
+storage. Their aim is to isolate a corrupted computer, using one of three methods:
- * Power - A computer that is switched off cannot corrupt data, but it is important to not do a "soft-reboot" as we won't know if this is possible. This also works for virtual machines when the fence device is a hypervisor.
- * Network - Switches can prevent routing to a given computer, so even if a computer is powered on it won't be able to harm the data.
+ * Power - A computer that is switched off cannot corrupt data, but it is important to not do a "soft-reboot" as we
+ won't know if this is possible. This also works for virtual machines when the fence device is a hypervisor.
+ * Network - Switches can prevent routing to a given computer, so even if a computer is powered on it won't be able to
+ harm the data.
* Configuration - Fibre-channel switches or SCSI devices allow us to limit who can write to managed disks.
-Fence agents do not use configuration files, as configuration management is outside of their scope. All of the configuration has to be specified either as command-line arguments or lines of standard input (see the complete list for more info).
+Fence agents do not use configuration files, as configuration management is outside of their scope. All of the
+configuration has to be specified either as command-line arguments or lines of standard input (see the complete list
+for more info).
-Because many fence agents are quite similar to each other, a fencing library (in Python) was developed. Please use it for further development. Creating or modifying a new fence agent should be quite simple using this library.
+Because many fence agents are quite similar to each other, a fencing library (in Python) was developed. Please use it
+for further development. Creating or modifying a new fence agent should be quite simple using this library.
## Where can I find more information?
* [ClusterLabs website](http://www.clusterlabs.org/)
* [User and developer documentation](https://github.com/ClusterLabs/fence-agents/tree/master/doc/FenceAgentAPI.md)
-* Mailing lists for [users](http://oss.clusterlabs.org/mailman/listinfo/users) and [developers](http://oss.clusterlabs.org/mailman/listinfo/developers)
+* Mailing lists for [users](http://oss.clusterlabs.org/mailman/listinfo/users) and
+ [developers](http://oss.clusterlabs.org/mailman/listinfo/developers)
* #clusterlabs IRC channel on [freenode](http://freenode.net/)
diff --git a/agents/eaton_ssh/fence_eaton_ssh.py b/agents/eaton_ssh/fence_eaton_ssh.py
new file mode 100644
index 00000000..8e536a2e
--- /dev/null
+++ b/agents/eaton_ssh/fence_eaton_ssh.py
@@ -0,0 +1,318 @@
+#!@PYTHON@ -tt
+
+"""
+Plug numbering starts with 1! There were no tests performed so far with daisy chained PDUs.
+
+Example usage:
+ fence_eaton_ssh -v -a <IP> -l <USER> -p <PASSWORD> --login-timeout=60 --action status --plug 1
+"""
+
+#####
+##
+## The Following Agent Has Been Tested On:
+##
+## Model Firmware
+## +---------------------------------------------+
+## EMAB04 04.02.0001
+#####
+
+import enum
+import sys
+import atexit
+
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, EC_STATUS, EC_LOGIN_DENIED
+
+
+class FenceEatonPowerActions(enum.Enum):
+ """
+ Status of the plug on the PDU.
+ """
+ ERROR = -1
+ OFF = 0
+ ON = 1
+ PENDING_OFF = 2
+ PENDING_ON = 3
+
+
+def get_plug_names(conn, plug_ids, command_prompt, shell_timout):
+ """
+ Get the names of plugs via their ID.
+
+ :param conn: The "fspawn" object.
+ :param plug_ids: The list of plug IDs. Plugs start with the ID 1.
+ :param command_prompt: The characters that make up the base prompt. This is important to detect a finished command.
+ :param shell_timeout: The maximum time the shell should wait for a response.
+ :returns: The name of the requested plugs.
+ """
+ # fspawn is subclassed from pexpect which is not correctly type annotated in all cases.
+ result = {}
+ full_node_mapping = {}
+ conn.send_eol("get PDU.OutletSystem.Outlet[x].iName")
+ conn.log_expect(command_prompt, shell_timout)
+ result_plug_names = conn.before.split("\n") # type: ignore
+ if len(result_plug_names) != 3:
+ fail(EC_STATUS)
+ plug_names = result_plug_names.split("|")
+ for counter in range(1, len(plug_names)):
+ full_node_mapping[counter] = plug_names[counter]
+ for plug_id in plug_ids:
+ result[plug_id] = full_node_mapping[plug_id]
+ return result
+
+
+def get_plug_ids(conn, nodenames, command_prompt, shell_timout):
+ """
+ Get the IDs that map to the given nodenames. Non existing names are skipped.
+
+ :param conn: The "fspawn" object.
+ :param nodenames: The list of human readable names that should be converted to IDs.
+ :param command_prompt: The characters that make up the base prompt. This is important to detect a finished command.
+ :param shell_timeout: The maximum time the shell should wait for a response.
+ :returns: A dictionary - possibly empty - where the keys are the node names and the values are the node IDs.
+ """
+ result = {}
+ full_node_mapping = {}
+ conn.send_eol("get PDU.OutletSystem.Outlet[x].iName")
+ conn.log_expect(command_prompt, shell_timout)
+ result_plug_names = conn.before.split("\n") # type: ignore
+ if len(result_plug_names) != 3:
+ fail(EC_STATUS)
+ plug_names = result_plug_names.split("|")
+ for counter in range(1, len(plug_names)):
+ full_node_mapping[plug_names[counter]] = counter
+ for node in nodenames:
+ if node in full_node_mapping:
+ result[node] = full_node_mapping[node]
+ return result
+
+
+def get_plug_count(conn, command_prompt, shell_timout):
+ """
+ Get the number of plugs that the PDU has.
+
+ In case the PDU is daisy chained this also contains the plugs of the other PDUs.
+
+ :param conn: The "fspawn" object.
+ :param command_prompt: The characters that make up the base prompt. This is important to detect a finished command.
+ :param shell_timeout: The maximum time the shell should wait for a response.
+ :returns: The number of plugs that the PDU has.
+ """
+ # fspawn is subclassed from pexpect which is not correctly type annotated in all cases.
+ conn.send_eol("get PDU.OutletSystem.Outlet.Count")
+ conn.log_expect(command_prompt, shell_timout)
+ result_plug_count = conn.before.split("\n") # type: ignore
+ if len(result_plug_count) != 3:
+ fail(EC_STATUS)
+ return int(result_plug_count[1].strip())
+
+
+def get_plug_status(conn, plug_id, command_prompt, shell_timout):
+ """
+ Get the current status of the plug. The return value of this doesn't account for operations that will act via
+ schedules or a delay. As such the status is only valid at the time of retrieval.
+
+ :param conn: The "fspawn" object.
+ :param plug_id: The ID of the plug that should be powered off. Counting plugs starts at 1.
+ :returns: The current status of the plug.
+ """
+ # fspawn is subclassed from pexpect which is not correctly type annotated in all cases.
+ conn.send_eol(f"get PDU.OutletSystem.Outlet[{plug_id}].PresentStatus.SwitchOnOff")
+ conn.log_expect(command_prompt, shell_timout)
+ result_plug_status = conn.before.split("\n") # type: ignore
+ if len(result_plug_status) != 3:
+ fail(EC_STATUS)
+ if result_plug_status[1].strip() == "0":
+ return FenceEatonPowerActions.OFF
+ elif result_plug_status[1].strip() == "1":
+ return FenceEatonPowerActions.ON
+ else:
+ return FenceEatonPowerActions.ERROR
+
+
+def power_on_plug(conn, plug_id, command_prompt, shell_timout, delay=0):
+ """
+ Powers on a plug with an optional delay.
+
+ :param conn: The "fspawn" object.
+ :param plug_id: The ID of the plug that should be powered off. Counting plugs starts at 1.
+ :param command_prompt: The characters that make up the base prompt. This is important to detect a finished command.
+ :param shell_timeout: The maximum time the shell should wait for a response.
+ :param delay: The delay in seconds. Passing "-1" aborts the power off action.
+ """
+ conn.send_eol(f"set PDU.OutletSystem.Outlet[{plug_id}].DelayBeforeStartup {delay}")
+ conn.log_expect(command_prompt, shell_timout)
+
+
+def power_off_plug(conn, plug_id, command_prompt, shell_timout, delay=0):
+ """
+ Powers off a plug with an optional delay.
+
+ :param conn: The "fspawn" object.
+ :param plug_id: The ID of the plug that should be powered off. Counting plugs starts at 1.
+ :param command_prompt: The characters that make up the base prompt. This is important to detect a finished command.
+ :param shell_timeout: The maximum time the shell should wait for a response.
+ :param delay: The delay in seconds. Passing "-1" aborts the power off action.
+ """
+ conn.send_eol(f"set PDU.OutletSystem.Outlet[{plug_id}].DelayBeforeShutdown {delay}")
+ conn.log_expect(command_prompt, shell_timout)
+
+
+def get_power_status(conn, options):
+ """
+ Retrieve the power status for the requested plug. Since we have a serial like interface via SSH we need to parse the
+ output of the SSH session manually.
+
+ If abnormal behavior is detected the method will exit via "fail()".
+
+ :param conn: The "fspawn" object.
+ :param options: The option dictionary.
+ :returns: In case there is an error this method does not return but instead calls "sys.exit". Otherwhise one of
+ "off", "on" or "error" is returned.
+ """
+ if conn is None:
+ fail(EC_LOGIN_DENIED)
+
+ requested_plug = options.get("--plug", "")
+ if not requested_plug:
+ fail(EC_STATUS)
+ plug_status = get_plug_status(
+ conn, # type: ignore
+ int(requested_plug),
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+ if plug_status == FenceEatonPowerActions.OFF:
+ return "off"
+ elif plug_status == FenceEatonPowerActions.ON:
+ return "on"
+ else:
+ return "error"
+
+
+def set_power_status(conn, options):
+ """
+ Set the power status for the requested plug. Only resposible for powering on and off.
+
+ If abnormal behavior is detected the method will exit via "fail()".
+
+ :param conn: The "fspawn" object.
+ :param options: The option dictionary.
+ :returns: In case there is an error this method does not return but instead calls "sys.exit".
+ """
+ if conn is None:
+ fail(EC_LOGIN_DENIED)
+
+ requested_plug = options.get("--plug", "")
+ if not requested_plug:
+ fail(EC_STATUS)
+ requested_action = options.get("--action", "")
+ if not requested_action:
+ fail(EC_STATUS)
+
+ if requested_action == "off":
+ power_off_plug(
+ conn, # type: ignore
+ int(requested_plug),
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+ elif requested_action == "on":
+ power_on_plug(
+ conn, # type: ignore
+ int(requested_plug),
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+ else:
+ fail(EC_STATUS)
+
+
+def get_outlet_list(conn, options):
+ """
+ Retrieves the list of plugs with their correspondin status.
+
+ :param conn: The "fspawn" object.
+ :param options: The option dictionary.
+ :returns: Keys are the Plug IDs which each have a Tuple with the alias for the plug and its status.
+ """
+ if conn is None:
+ fail(EC_LOGIN_DENIED)
+
+ result = {}
+ plug_count = get_plug_count(conn, options["--command-prompt"], int(options["--shell-timeout"])) # type: ignore
+ for counter in range(1, plug_count):
+ plug_names = get_plug_names(
+ conn, # type: ignore
+ [counter],
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+ plug_status_enum = get_plug_status(
+ conn, # type: ignore
+ counter,
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+ if plug_status_enum == FenceEatonPowerActions.OFF:
+ plug_status = "OFF"
+ elif plug_status_enum == FenceEatonPowerActions.ON:
+ plug_status = "ON"
+ else:
+ plug_status = None
+ result[str(counter)] = (plug_names[counter], plug_status)
+ return result
+
+
+def reboot_cycle(conn, options) -> None:
+ """
+ Responsible for power cycling a machine. Not responsible for singular on and off actions.
+
+ :param conn: The "fspawn" object.
+ :param options: The option dictionary.
+ """
+ requested_plug = options.get("--plug", "")
+ if not requested_plug:
+ fail(EC_STATUS)
+
+ power_off_plug(
+ conn, # type: ignore
+ int(requested_plug),
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+ power_on_plug(
+ conn, # type: ignore
+ int(requested_plug),
+ options["--command-prompt"],
+ int(options["--shell-timeout"])
+ )
+
+
+def main():
+ """
+ Main entrypoint for the fence_agent.
+ """
+ device_opt = ["secure", "ipaddr", "login", "passwd", "port", "cmd_prompt"]
+ atexit.register(atexit_handler)
+ options = check_input(device_opt, process_input(device_opt))
+ options["--ssh"] = None
+ options["--ipport"] = 22
+ options["--command-prompt"] = "pdu#0>"
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for Eaton ePDU G3 over SSH"
+ docs["longdesc"] = "fence_eaton_ssh is a fence agent that connects to Eaton ePDU devices. It logs into \
+device via ssh and reboot a specified outlet."
+ docs["vendorurl"] = "https://www.eaton.com/"
+ show_docs(options, docs)
+
+ conn = fence_login(options)
+ result = fence_action(conn, options, set_power_status, get_power_status, get_outlet_list, reboot_cycle)
+ fence_logout(conn, "quit")
+ sys.exit(result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/configure.ac b/configure.ac
index 65a9718d..8436ba25 100644
--- a/configure.ac
+++ b/configure.ac
@@ -567,7 +567,7 @@ if test "x$VERSION" = "xUNKNOWN"; then
configure was unable to determine the source tree's current version. This
generally happens when using git archive (or the github download button)
generated tarball/zip file. In order to workaround this issue, either use git
- clone https://github.com/ClusterLabs/fence-virt.git or use an official release
+ clone https://github.com/ClusterLabs/fence-agents.git or use an official release
tarball. Alternatively you can add a compatible version in a .tarball-version
file at the top of the source tree, wipe your autom4te.cache dir and generated
configure, and rerun autogen.sh.
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 00000000..6ac9480c
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,34 @@
+FROM opensuse/leap:15.5
+
+RUN zypper in -y \
+ git \
+ autoconf \
+ automake \
+ libtool \
+ make \
+ gcc \
+ libcorosync-devel \
+ libxslt1 \
+ libxslt-tools \
+ python3-devel \
+ python3-httplib2 \
+ python3-pexpect \
+ python3-pycurl \
+ python3-requests \
+ python3-suds-jurko \
+ python3-openwsman \
+ python3-boto3 \
+ python3-novaclient \
+ python3-keystoneclient \
+ mozilla-nss-devel \
+ mozilla-nspr-devel \
+ libvirt-devel \
+ libxml2-devel \
+ flex \
+ bison \
+ libuuid-devel \
+ systemd
+
+WORKDIR /code
+VOLUME /code
+ENTRYPOINT ["./docker/entrypoint.sh"]
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 00000000..2aef7421
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,10 @@
+# Dockerfile to build the fence-agents locally
+
+Usage is as follows:
+
+```
+podman build -f docker/Dockerfile -t fence-agents:main .
+podman run -it -v $PWD:/code --rm localhost/fence-agents:main
+```
+
+In case you are running docker replace `podman` with `docker` and it should work the same.
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
new file mode 100755
index 00000000..caa778e2
--- /dev/null
+++ b/docker/entrypoint.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/bash
+
+echo "### Running autogen"
+./autogen.sh
+echo "### Running configure"
+./configure
+echo "### Running make"
+make
diff --git a/fence-agents.spec.in b/fence-agents.spec.in
index b6af20d9..343f1c1a 100644
--- a/fence-agents.spec.in
+++ b/fence-agents.spec.in
@@ -55,6 +55,7 @@ fence-agents-docker \\
fence-agents-drac \\
fence-agents-drac5 \\
fence-agents-eaton-snmp \\
+fence-agents-eaton-ssh \\
fence-agents-ecloud \\
fence-agents-emerson \\
fence-agents-eps \\
@@ -631,6 +632,18 @@ via the SNMP protocol.
%{_sbindir}/fence_eaton_snmp
%{_mandir}/man8/fence_eaton_snmp.8*
+%package eaton-ssh
+License: GPL-2.0-or-later AND LGPL-2.0-or-later
+Summary: Fence agent for Eaton network power switches
+Requires: fence-agents-common = %{version}-%{release}
+BuildArch: noarch
+%description eaton-ssh
+Fence agent for Eaton network power switches that are accessed
+via the serial protocol tunnel over SSH.
+%files eaton-ssh
+%{_sbindir}/fence_eaton_ssh
+%{_mandir}/man8/fence_eaton_ssh.8*
+
%package ecloud
License: GPL-2.0-or-later AND LGPL-2.0-or-later
Summary: Fence agent for eCloud and eCloud VPC
diff --git a/tests/data/metadata/fence_eaton_ssh.xml b/tests/data/metadata/fence_eaton_ssh.xml
new file mode 100644
index 00000000..a3be1ac6
--- /dev/null
+++ b/tests/data/metadata/fence_eaton_ssh.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0" ?>
+<resource-agent name="fence_eaton_ssh" shortdesc="Fence agent for Eaton ePDU G3 over SSH" >
+<longdesc>fence_eaton_ssh is a fence agent that connects to Eaton ePDU devices. It logs into device via ssh and reboot a specified outlet.</longdesc>
+<vendor-url>https://www.eaton.com/</vendor-url>
+<parameters>
+ <parameter name="action" unique="0" required="1">
+ <getopt mixed="-o, --action=[action]" />
+ <content type="string" default="reboot" />
+ <shortdesc lang="en">Fencing action</shortdesc>
+ </parameter>
+ <parameter name="cmd_prompt" unique="0" required="0" deprecated="1">
+ <getopt mixed="-c, --command-prompt=[prompt]" />
+ <content type="string" />
+ <shortdesc lang="en">Force Python regex for command prompt</shortdesc>
+ </parameter>
+ <parameter name="command_prompt" unique="0" required="0" obsoletes="cmd_prompt">
+ <getopt mixed="-c, --command-prompt=[prompt]" />
+ <content type="string" />
+ <shortdesc lang="en">Force Python regex for command prompt</shortdesc>
+ </parameter>
+ <parameter name="identity_file" unique="0" required="0">
+ <getopt mixed="-k, --identity-file=[filename]" />
+ <shortdesc lang="en">Identity file (private key) for SSH</shortdesc>
+ </parameter>
+ <parameter name="inet4_only" unique="0" required="0">
+ <getopt mixed="-4, --inet4-only" />
+ <content type="boolean" />
+ <shortdesc lang="en">Forces agent to use IPv4 addresses only</shortdesc>
+ </parameter>
+ <parameter name="inet6_only" unique="0" required="0">
+ <getopt mixed="-6, --inet6-only" />
+ <content type="boolean" />
+ <shortdesc lang="en">Forces agent to use IPv6 addresses only</shortdesc>
+ </parameter>
+ <parameter name="ip" unique="0" required="1" obsoletes="ipaddr">
+ <getopt mixed="-a, --ip=[ip]" />
+ <content type="string" />
+ <shortdesc lang="en">IP address or hostname of fencing device</shortdesc>
+ </parameter>
+ <parameter name="ipaddr" unique="0" required="1" deprecated="1">
+ <getopt mixed="-a, --ip=[ip]" />
+ <content type="string" />
+ <shortdesc lang="en">IP address or hostname of fencing device</shortdesc>
+ </parameter>
+ <parameter name="ipport" unique="0" required="0">
+ <getopt mixed="-u, --ipport=[port]" />
+ <content type="integer" />
+ <shortdesc lang="en">TCP/UDP port to use for connection with device</shortdesc>
+ </parameter>
+ <parameter name="login" unique="0" required="1" deprecated="1">
+ <getopt mixed="-l, --username=[name]" />
+ <content type="string" />
+ <shortdesc lang="en">Login name</shortdesc>
+ </parameter>
+ <parameter name="passwd" unique="0" required="0" deprecated="1">
+ <getopt mixed="-p, --password=[password]" />
+ <content type="string" />
+ <shortdesc lang="en">Login password or passphrase</shortdesc>
+ </parameter>
+ <parameter name="passwd_script" unique="0" required="0" deprecated="1">
+ <getopt mixed="-S, --password-script=[script]" />
+ <content type="string" />
+ <shortdesc lang="en">Script to run to retrieve password</shortdesc>
+ </parameter>
+ <parameter name="password" unique="0" required="0" obsoletes="passwd">
+ <getopt mixed="-p, --password=[password]" />
+ <content type="string" />
+ <shortdesc lang="en">Login password or passphrase</shortdesc>
+ </parameter>
+ <parameter name="password_script" unique="0" required="0" obsoletes="passwd_script">
+ <getopt mixed="-S, --password-script=[script]" />
+ <content type="string" />
+ <shortdesc lang="en">Script to run to retrieve password</shortdesc>
+ </parameter>
+ <parameter name="plug" unique="0" required="1" obsoletes="port">
+ <getopt mixed="-n, --plug=[id]" />
+ <content type="string" />
+ <shortdesc lang="en">Physical plug number on device, UUID or identification of machine</shortdesc>
+ </parameter>
+ <parameter name="port" unique="0" required="1" deprecated="1">
+ <getopt mixed="-n, --plug=[id]" />
+ <content type="string" />
+ <shortdesc lang="en">Physical plug number on device, UUID or identification of machine</shortdesc>
+ </parameter>
+ <parameter name="secure" unique="0" required="0" deprecated="1">
+ <getopt mixed="-x, --ssh" />
+ <content type="boolean" />
+ <shortdesc lang="en">Use SSH connection</shortdesc>
+ </parameter>
+ <parameter name="ssh" unique="0" required="0" obsoletes="secure">
+ <getopt mixed="-x, --ssh" />
+ <content type="boolean" />
+ <shortdesc lang="en">Use SSH connection</shortdesc>
+ </parameter>
+ <parameter name="ssh_options" unique="0" required="0">
+ <getopt mixed="--ssh-options=[options]" />
+ <content type="string" />
+ <shortdesc lang="en">SSH options to use</shortdesc>
+ </parameter>
+ <parameter name="username" unique="0" required="1" obsoletes="login">
+ <getopt mixed="-l, --username=[name]" />
+ <content type="string" />
+ <shortdesc lang="en">Login name</shortdesc>
+ </parameter>
+ <parameter name="quiet" unique="0" required="0">
+ <getopt mixed="-q, --quiet" />
+ <content type="boolean" />
+ <shortdesc lang="en">Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog.</shortdesc>
+ </parameter>
+ <parameter name="verbose" unique="0" required="0">
+ <getopt mixed="-v, --verbose" />
+ <content type="boolean" />
+ <shortdesc lang="en">Verbose mode. Multiple -v flags can be stacked on the command line (e.g., -vvv) to increase verbosity.</shortdesc>
+ </parameter>
+ <parameter name="verbose_level" unique="0" required="0">
+ <getopt mixed="--verbose-level" />
+ <content type="integer" />
+ <shortdesc lang="en">Level of debugging detail in output. Defaults to the number of --verbose flags specified on the command line, or to 1 if verbose=1 in a stonith device configuration (i.e., on stdin).</shortdesc>
+ </parameter>
+ <parameter name="debug" unique="0" required="0" deprecated="1">
+ <getopt mixed="-D, --debug-file=[debugfile]" />
+ <content type="string" />
+ <shortdesc lang="en">Write debug information to given file</shortdesc>
+ </parameter>
+ <parameter name="debug_file" unique="0" required="0" obsoletes="debug">
+ <getopt mixed="-D, --debug-file=[debugfile]" />
+ <shortdesc lang="en">Write debug information to given file</shortdesc>
+ </parameter>
+ <parameter name="version" unique="0" required="0">
+ <getopt mixed="-V, --version" />
+ <content type="boolean" />
+ <shortdesc lang="en">Display version information and exit</shortdesc>
+ </parameter>
+ <parameter name="help" unique="0" required="0">
+ <getopt mixed="-h, --help" />
+ <content type="boolean" />
+ <shortdesc lang="en">Display help and exit</shortdesc>
+ </parameter>
+ <parameter name="plug_separator" unique="0" required="0">
+ <getopt mixed="--plug-separator=[char]" />
+ <content type="string" default="," />
+ <shortdesc lang="en">Separator for plug parameter when specifying more than 1 plug</shortdesc>
+ </parameter>
+ <parameter name="separator" unique="0" required="0">
+ <getopt mixed="-C, --separator=[char]" />
+ <content type="string" default="," />
+ <shortdesc lang="en">Separator for CSV created by 'list' operation</shortdesc>
+ </parameter>
+ <parameter name="delay" unique="0" required="0">
+ <getopt mixed="--delay=[seconds]" />
+ <content type="second" default="0" />
+ <shortdesc lang="en">Wait X seconds before fencing is started</shortdesc>
+ </parameter>
+ <parameter name="disable_timeout" unique="0" required="0">
+ <getopt mixed="--disable-timeout=[true/false]" />
+ <content type="string" />
+ <shortdesc lang="en">Disable timeout (true/false) (default: true when run from Pacemaker 2.0+)</shortdesc>
+ </parameter>
+ <parameter name="login_timeout" unique="0" required="0">
+ <getopt mixed="--login-timeout=[seconds]" />
+ <content type="second" default="5" />
+ <shortdesc lang="en">Wait X seconds for cmd prompt after login</shortdesc>
+ </parameter>
+ <parameter name="power_timeout" unique="0" required="0">
+ <getopt mixed="--power-timeout=[seconds]" />
+ <content type="second" default="20" />
+ <shortdesc lang="en">Test X seconds for status change after ON/OFF</shortdesc>
+ </parameter>
+ <parameter name="power_wait" unique="0" required="0">
+ <getopt mixed="--power-wait=[seconds]" />
+ <content type="second" default="0" />
+ <shortdesc lang="en">Wait X seconds after issuing ON/OFF</shortdesc>
+ </parameter>
+ <parameter name="shell_timeout" unique="0" required="0">
+ <getopt mixed="--shell-timeout=[seconds]" />
+ <content type="second" default="3" />
+ <shortdesc lang="en">Wait X seconds for cmd prompt after issuing command</shortdesc>
+ </parameter>
+ <parameter name="stonith_status_sleep" unique="0" required="0">
+ <getopt mixed="--stonith-status-sleep=[seconds]" />
+ <content type="second" default="1" />
+ <shortdesc lang="en">Sleep X seconds between status calls during a STONITH action</shortdesc>
+ </parameter>
+ <parameter name="retry_on" unique="0" required="0">
+ <getopt mixed="--retry-on=[attempts]" />
+ <content type="integer" default="1" />
+ <shortdesc lang="en">Count of attempts to retry power on</shortdesc>
+ </parameter>
+ <parameter name="ssh_path" unique="0" required="0">
+ <getopt mixed="--ssh-path=[path]" />
+ <shortdesc lang="en">Path to ssh binary</shortdesc>
+ </parameter>
+</parameters>
+<actions>
+ <action name="on" automatic="0"/>
+ <action name="off" />
+ <action name="reboot" />
+ <action name="status" />
+ <action name="list" />
+ <action name="list-status" />
+ <action name="monitor" />
+ <action name="metadata" />
+ <action name="manpage" />
+ <action name="validate-all" />
+</actions>
+</resource-agent>
--
2.25.1