All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ondrej Mular <omular@redhat.com>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] [PATCH] fence_docker: new fence agent for Docker	containers
Date: Thu, 26 Jun 2014 06:38:51 -0400 (EDT)	[thread overview]
Message-ID: <1667975334.23429151.1403779131766.JavaMail.zimbra@redhat.com> (raw)
In-Reply-To: <131522850.16569170.1402325011684.JavaMail.zimbra@redhat.com>

With support for TLS authentication.

---
 .gitignore                           |   1 +
 configure.ac                         |   1 +
 fence/agents/docker/Makefile.am      |  20 +++++
 fence/agents/docker/fence_docker.py  | 152 +++++++++++++++++++++++++++++++++++
 tests/data/metadata/fence_docker.xml | 129 +++++++++++++++++++++++++++++
 5 files changed, 303 insertions(+)
 create mode 100644 fence/agents/docker/Makefile.am
 create mode 100644 fence/agents/docker/fence_docker.py
 create mode 100644 tests/data/metadata/fence_docker.xml

diff --git a/.gitignore b/.gitignore
index 75b9aa7..8bcae2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,7 @@ fence/agents/bullpap/fence_bullpap
 fence/agents/cisco_mds/fence_cisco_mds
 fence/agents/cisco_ucs/fence_cisco_ucs
 fence/agents/cpint/fence_cpint
+fence/agents/docker/fence_docker
 fence/agents/drac/fence_drac
 fence/agents/drac5/fence_drac5
 fence/agents/eaton_snmp/fence_eaton_snmp
diff --git a/configure.ac b/configure.ac
index 4d0aa6a..bc9785b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -264,6 +264,7 @@ AC_CONFIG_FILES([Makefile
 		 fence/agents/brocade/Makefile
 		 fence/agents/cisco_mds/Makefile
 		 fence/agents/cisco_ucs/Makefile
+		 fence/agents/docker/Makefile
 		 fence/agents/drac/Makefile
 		 fence/agents/drac5/Makefile
 		 fence/agents/dummy/Makefile
diff --git a/fence/agents/docker/Makefile.am b/fence/agents/docker/Makefile.am
new file mode 100644
index 0000000..a232902
--- /dev/null
+++ b/fence/agents/docker/Makefile.am
@@ -0,0 +1,20 @@
+MAINTAINERCLEANFILES	= Makefile.in
+
+TARGET			= fence_docker
+
+SRC			= $(TARGET).py
+
+EXTRA_DIST		= $(SRC)
+
+sbin_SCRIPTS		= $(TARGET)
+
+man_MANS		= $(TARGET).8
+
+FENCE_TEST_ARGS		= -a test -n 1
+
+include $(top_srcdir)/make/fencebuild.mk
+include $(top_srcdir)/make/fenceman.mk
+include $(top_srcdir)/make/agentpycheck.mk
+
+clean-local: clean-man
+	rm -f $(TARGET)
diff --git a/fence/agents/docker/fence_docker.py b/fence/agents/docker/fence_docker.py
new file mode 100644
index 0000000..7363611
--- /dev/null
+++ b/fence/agents/docker/fence_docker.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python -tt
+
+import atexit
+import sys
+import StringIO
+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
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION = ""
+REDHAT_COPYRIGHT = ""
+BUILD_DATE = ""
+#END_VERSION_GENERATION
+
+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"]] = (container["Names"][0], {True:"off", False: "on"}[container["Status"][:4].lower() == "exit"])
+	return containers
+
+
+def send_cmd(options, cmd, post = False):
+	url = "http%s://%s:%s/v1.11/%s" % ("s" if "--ssl" in options else "", options["--ip"], options["--ipport"], cmd)
+	conn = pycurl.Curl()
+	output_buffer = StringIO.StringIO()
+	if logging.getLogger().getEffectiveLevel() < logging.WARNING:
+		conn.setopt(pycurl.VERBOSE, True)
+	conn.setopt(pycurl.HTTPGET, 1)
+	conn.setopt(pycurl.URL, str(url))
+	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" 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"])
+	else:
+		conn.setopt(pycurl.SSL_VERIFYPEER, 0)
+		conn.setopt(pycurl.SSL_VERIFYHOST, 0)
+
+	logging.debug("URL: " + url)
+
+	try:
+		conn.perform()
+		result = output_buffer.getvalue()
+		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:
+		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
+	}
+
+	device_opt = ["ipaddr", "no_password", "no_login", "port", "method", "web", "tlscert", "tlskey", "tlscacert", "ssl"]
+
+	options = check_input(device_opt, process_input(device_opt))
+
+	docs = { }
+	docs["shortdesc"] = "Fence agent for Docker"
+	docs["longdesc"] = "fence_docker is I/O 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
new file mode 100644
index 0000000..bda31d01
--- /dev/null
+++ b/tests/data/metadata/fence_docker.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" ?>
+<resource-agent name="fence_docker" shortdesc="Fence agent for Docker" >
+<longdesc>fence_docker is I/O 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="ipport" unique="0" required="0">
+		<getopt mixed="-u, --ipport=[port]" />
+		<content type="string" default="80"  />
+		<shortdesc lang="en">TCP/UDP port to use for connection with device</shortdesc>
+	</parameter>
+	<parameter name="port" unique="0" required="1">
+		<getopt mixed="-n, --plug=[id]" />
+		<content type="string"  />
+		<shortdesc lang="en">Physical plug number, name of virtual machine or UUID</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="ipaddr" unique="0" required="1">
+		<getopt mixed="-a, --ip=[ip]" />
+		<content type="string"  />
+		<shortdesc lang="en">IP Address or Hostname</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="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 (onoff|cycle)</shortdesc>
+	</parameter>
+	<parameter name="ssl" unique="0" required="0">
+		<getopt mixed="-z, --ssl" />
+		<content type="boolean"  />
+		<shortdesc lang="en">SSL connection</shortdesc>
+	</parameter>
+	<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="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="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="verbose" unique="0" required="0">
+		<getopt mixed="-v, --verbose" />
+		<content type="boolean"  />
+		<shortdesc lang="en">Verbose mode</shortdesc>
+	</parameter>
+	<parameter name="debug" unique="0" required="0">
+		<getopt mixed="-D, --debug-file=[debugfile]" />
+		<content type="string"  />
+		<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="separator" unique="0" required="0">
+		<getopt mixed="-C, --separator=[char]" />
+		<content type="string" default=","  />
+		<shortdesc lang="en">Separator for CSV created by operation list</shortdesc>
+	</parameter>
+	<parameter name="power_wait" unique="0" required="0">
+		<getopt mixed="--power-wait=[seconds]" />
+		<content type="string" default="0"  />
+		<shortdesc lang="en">Wait X seconds after issuing ON/OFF</shortdesc>
+	</parameter>
+	<parameter name="power_timeout" unique="0" required="0">
+		<getopt mixed="--power-timeout=[seconds]" />
+		<content type="string" default="20"  />
+		<shortdesc lang="en">Test X seconds for status change after ON/OFF</shortdesc>
+	</parameter>
+	<parameter name="delay" unique="0" required="0">
+		<getopt mixed="--delay=[seconds]" />
+		<content type="string" default="0"  />
+		<shortdesc lang="en">Wait X seconds before fencing is started</shortdesc>
+	</parameter>
+	<parameter name="login_timeout" unique="0" required="0">
+		<getopt mixed="--login-timeout=[seconds]" />
+		<content type="string" default="5"  />
+		<shortdesc lang="en">Wait X seconds for cmd prompt after login</shortdesc>
+	</parameter>
+	<parameter name="shell_timeout" unique="0" required="0">
+		<getopt mixed="--shell-timeout=[seconds]" />
+		<content type="string" default="3"  />
+		<shortdesc lang="en">Wait X seconds for cmd prompt after issuing command</shortdesc>
+	</parameter>
+	<parameter name="retry_on" unique="0" required="0">
+		<getopt mixed="--retry-on=[attempts]" />
+		<content type="string" default="1"  />
+		<shortdesc lang="en">Count of attempts to retry power on</shortdesc>
+	</parameter>
+</parameters>
+<actions>
+	<action name="on" automatic="0"/>
+	<action name="off" />
+	<action name="reboot" />
+	<action name="status" />
+	<action name="list" />
+	<action name="monitor" />
+	<action name="metadata" />
+</actions>
+</resource-agent>
-- 
1.8.3.1



      parent reply	other threads:[~2014-06-26 10:38 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <848848437.16562712.1402324499040.JavaMail.zimbra@redhat.com>
2014-06-09 14:43 ` [Cluster-devel] [PATCH] fence_docker: new fence agent for Docker containers Ondrej Mular
2014-06-09 18:09   ` SGhosh
2014-06-26 10:38   ` Ondrej Mular [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1667975334.23429151.1403779131766.JavaMail.zimbra@redhat.com \
    --to=omular@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.