diff --git a/agents/docker/fence_docker.py b/agents/docker/fence_docker.py index 8042515a..5ba4d0e7 100644 --- a/agents/docker/fence_docker.py +++ b/agents/docker/fence_docker.py @@ -1,161 +1,186 @@ #!@PYTHON@ -tt import atexit import sys import io import logging import pycurl import json sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import fail_usage, all_opt, fence_action, atexit_handler, check_input, process_input, show_docs, run_delay def get_power_status(conn, options): del conn status = send_cmd(options, "containers/%s/json" % options["--plug"]) if status is None: return None return "on" if status["State"]["Running"] else "off" def set_power_status(conn, options): del conn if options["--action"] == "on": send_cmd(options, "containers/%s/start" % options["--plug"], True) else: send_cmd(options, "containers/%s/kill" % options["--plug"], True) return def reboot_cycle(conn, options): del conn send_cmd(options, "containers/%s/restart" % options["--plug"], True) return get_power_status(conn, options) def get_list(conn, options): del conn output = send_cmd(options, "containers/json?all=1") containers = {} for container in output: containers[container["Id"]] = ({True:container["Names"][0][1:], False: container["Names"][0]}[container["Names"][0][0:1] == '/'], {True:"off", False: "on"}[container["Status"][:4].lower() == "exit"]) return containers def send_cmd(options, cmd, post = False): url = "http%s://%s:%s/v%s/%s" % ("s" if "--ssl-secure" in options or "--ssl-insecure" in options else "", options["--ip"], options["--ipport"], options["--api-version"], cmd) conn = pycurl.Curl() output_buffer = io.BytesIO() if logging.getLogger().getEffectiveLevel() < logging.WARNING: conn.setopt(pycurl.VERBOSE, True) + if "--unix-socket" in options: + conn.setopt(pycurl.UNIX_SOCKET_PATH, options["--unix-socket"]) conn.setopt(pycurl.HTTPGET, 1) conn.setopt(pycurl.URL, url.encode("ascii")) if post: conn.setopt(pycurl.POST, 1) conn.setopt(pycurl.POSTFIELDSIZE, 0) conn.setopt(pycurl.WRITEFUNCTION, output_buffer.write) conn.setopt(pycurl.TIMEOUT, int(options["--shell-timeout"])) if "--ssl-secure" in options: if not (set(("--tlscert", "--tlskey", "--tlscacert")) <= set(options)): fail_usage("Failed. If --ssl option is used, You have to also \ specify: --tlscert, --tlskey and --tlscacert") conn.setopt(pycurl.SSL_VERIFYPEER, 1) conn.setopt(pycurl.SSLCERT, options["--tlscert"]) conn.setopt(pycurl.SSLKEY, options["--tlskey"]) conn.setopt(pycurl.CAINFO, options["--tlscacert"]) elif "--ssl-insecure" in options: conn.setopt(pycurl.SSL_VERIFYPEER, 0) conn.setopt(pycurl.SSL_VERIFYHOST, 0) logging.debug("URL: " + url) try: conn.perform() result = output_buffer.getvalue().decode() return_code = conn.getinfo(pycurl.RESPONSE_CODE) logging.debug("RESULT [" + str(return_code) + \ "]: " + result) conn.close() if return_code == 200: return json.loads(result) except pycurl.error: logging.error("Connection failed") except: if result is not None: logging.error(result) logging.error("Cannot parse json") return None def main(): atexit.register(atexit_handler) all_opt["tlscert"] = { "getopt" : ":", "longopt" : "tlscert", "help" : "--tlscert " "Path to client certificate for TLS authentication", "required" : "0", "shortdesc" : "Path to client certificate (PEM format) \ for TLS authentication. Required if --ssl option is used.", "order": 2 } all_opt["tlskey"] = { "getopt" : ":", "longopt" : "tlskey", "help" : "--tlskey " "Path to client key for TLS authentication", "required" : "0", "shortdesc" : "Path to client key (PEM format) for TLS \ authentication. Required if --ssl option is used.", "order": 2 } all_opt["tlscacert"] = { "getopt" : ":", "longopt" : "tlscacert", "help" : "--tlscacert " "Path to CA certificate for TLS authentication", "required" : "0", "shortdesc" : "Path to CA certificate (PEM format) for \ TLS authentication. Required if --ssl option is used.", "order": 2 } all_opt["api_version"] = { "getopt" : ":", "longopt" : "api-version", "help" : "--api-version " "Version of Docker Remote API (default: 1.11)", "required" : "0", "order" : 2, "default" : "1.11", } - device_opt = ["ipaddr", "no_password", "no_login", "port", "method", "web", "tlscert", "tlskey", "tlscacert", "ssl", "api_version"] + all_opt["unix_socket"] = { + "getopt" : ":", + "longopt" : "unix-socket", + "help" : "--unix-socket " + "Path to Docker's unix socket. Use this with --disable-ssl.", + "required" : "0", + "order" : 2, + } - all_opt["ssl"]["default"] = "1" + all_opt["disable_ssl"] = { + "getopt" : "", + "longopt" : "disable-ssl", + "help" : "--disable-ssl Don't use SSL connection", + "required" : "0", + "shortdesc" : "Don't use SSL", + "order": 2, + } + device_opt = ["ipaddr", "no_password", "no_login", "port", "method", "web", + "tlscert", "tlskey", "tlscacert", "ssl", "api_version", "unix_socket", + "disable_ssl"] + all_opt["ssl"]["default"] = "1" options = check_input(device_opt, process_input(device_opt)) + if "--disable-ssl" in options or options["--ssl"] == "0": + for k in ["--ssl", "--ssl-secure", "--ssl-insecure"]: + if k in options: + del options[k] + docs = { } docs["shortdesc"] = "Fence agent for Docker" docs["longdesc"] = "fence_docker is a Power Fencing agent which \ can be used with the Docker Engine containers. You can use this \ fence-agent without any authentication, or you can use TLS authentication \ (use --ssl option, more info about TLS authentication in docker: \ http://docs.docker.com/examples/https/)." docs["vendorurl"] = "www.docker.io" show_docs(options, docs) run_delay(options) result = fence_action(None, options, set_power_status, get_power_status, get_list, reboot_cycle) sys.exit(result) if __name__ == "__main__": main() diff --git a/tests/data/metadata/fence_docker.xml b/tests/data/metadata/fence_docker.xml index f0cacd4d..670b7905 100644 --- a/tests/data/metadata/fence_docker.xml +++ b/tests/data/metadata/fence_docker.xml @@ -1,180 +1,190 @@ <?xml version="1.0" ?> <resource-agent name="fence_docker" shortdesc="Fence agent for Docker" > <longdesc>fence_docker is a Power Fencing agent which can be used with the Docker Engine containers. You can use this fence-agent without any authentication, or you can use TLS authentication (use --ssl option, more info about TLS authentication in docker: http://docs.docker.com/examples/https/).</longdesc> <vendor-url>www.docker.io</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="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" default="443" /> <shortdesc lang="en">TCP/UDP port to use for connection with device</shortdesc> </parameter> <parameter name="method" unique="0" required="0"> <getopt mixed="-m, --method=[method]" /> <content type="select" default="onoff" > <option value="onoff" /> <option value="cycle" /> </content> <shortdesc lang="en">Method to fence</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="ssl" unique="0" required="0"> <getopt mixed="-z, --ssl" /> <content type="boolean" default="1" /> <shortdesc lang="en">Use SSL connection with verifying certificate</shortdesc> </parameter> <parameter name="ssl_insecure" unique="0" required="0"> <getopt mixed="--ssl-insecure" /> <content type="boolean" /> <shortdesc lang="en">Use SSL connection without verifying certificate</shortdesc> </parameter> <parameter name="ssl_secure" unique="0" required="0"> <getopt mixed="--ssl-secure" /> <content type="boolean" /> <shortdesc lang="en">Use SSL connection with verifying certificate</shortdesc> </parameter> <parameter name="api_version" unique="0" required="0"> <getopt mixed="--api-version" /> <content type="string" default="1.11" /> <shortdesc lang="en">Version of Docker Remote API (default: 1.11)</shortdesc> </parameter> + <parameter name="disable_ssl" unique="0" required="0"> + <getopt mixed="--disable-ssl" /> + <content type="boolean" /> + <shortdesc lang="en">Don't use SSL</shortdesc> + </parameter> <parameter name="tlscacert" unique="0" required="0"> <getopt mixed="--tlscacert" /> <content type="string" /> <shortdesc lang="en">Path to CA certificate (PEM format) for TLS authentication. Required if --ssl option is used.</shortdesc> </parameter> <parameter name="tlscert" unique="0" required="0"> <getopt mixed="--tlscert" /> <content type="string" /> <shortdesc lang="en">Path to client certificate (PEM format) for TLS authentication. Required if --ssl option is used.</shortdesc> </parameter> <parameter name="tlskey" unique="0" required="0"> <getopt mixed="--tlskey" /> <content type="string" /> <shortdesc lang="en">Path to client key (PEM format) for TLS authentication. Required if --ssl option is used.</shortdesc> </parameter> + <parameter name="unix_socket" unique="0" required="0"> + <getopt mixed="--unix-socket" /> + <content type="string" /> + <shortdesc lang="en">Path to Docker's unix socket. Use this with --disable-ssl.</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="gnutlscli_path" unique="0" required="0"> <getopt mixed="--gnutlscli-path=[path]" /> <shortdesc lang="en">Path to gnutls-cli 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>