Gitweb:
http://git.fedorahosted.org/git/?p=fence-agents.git;a=commitdiff;h=f5401f...
Commit: f5401fc8dad9a39da0a279b7d55149dc0ebcba26
Parent: 028f0bba76b2171a3c25f5d755209ecb29c8053d
Author: Marek 'marx' Grac <mgrac(a)redhat.com>
AuthorDate: Mon Jan 5 10:15:36 2015 +0100
Committer: Marek 'marx' Grac <mgrac(a)redhat.com>
CommitterDate: Mon Jan 5 10:18:34 2015 +0100
fence_zvmip: Port fence agent to fencing library
This rewrite adds a correct result codes, support for 'list' action and
autogenerated manual page. The original code is still part of codebase as
new agent requires additional testing.
---
fence/agents/lib/fencing.py.py | 4 +
fence/agents/zvm/Makefile.am | 26 ++----
fence/agents/zvm/fence_zvmip.8 | 92 -------------------
fence/agents/zvm/fence_zvmip.py | 172 +++++++++++++++++++++++++++++++++++
tests/data/metadata/fence_zvmip.xml | 165 +++++++++++++++++++++++++--------
5 files changed, 310 insertions(+), 149 deletions(-)
diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py
index a8ea448..83bbfcd 100644
--- a/fence/agents/lib/fencing.py.py
+++ b/fence/agents/lib/fencing.py.py
@@ -7,6 +7,7 @@ import subprocess
import threading
import shlex
import exceptions
+import socket
import __main__
## do not add code here.
@@ -805,6 +806,9 @@ def fence_action(connection, options, set_power_fn, get_power_fn,
get_outlet_lis
except pycurl.error, ex:
logging.error("%s\n", str(ex))
fail(EC_TIMED_OUT)
+ except socket.timeout, ex:
+ logging.error("%s\n", str(ex))
+ fail(EC_TIMED_OUT)
return result
diff --git a/fence/agents/zvm/Makefile.am b/fence/agents/zvm/Makefile.am
index 62eb862..a29754d 100644
--- a/fence/agents/zvm/Makefile.am
+++ b/fence/agents/zvm/Makefile.am
@@ -2,26 +2,16 @@ MAINTAINERCLEANFILES = Makefile.in
TARGET = fence_zvmip
-sbin_PROGRAMS = fence_zvm fence_zvmip
+SRC = $(TARGET).py
-noinst_HEADERS = fence_zvm.h
+EXTRA_DIST = $(SRC)
-fence_zvm_SOURCES = fence_zvm.c
-fence_zvm_CFLAGS = -D_GNU_SOURCE
+sbin_SCRIPTS = $(TARGET)
-fence_zvmip_SOURCES = fence_zvmip.c
-fence_zvmip_CFLAGS = -D_GNU_SOURCE
+man_MANS = $(TARGET).8
-dist_man_MANS = fence_zvm.8 fence_zvmip.8
+FENCE_TEST_ARGS = -l test -p test -a test -n 1
-#include $(top_srcdir)/make/fencemanc.mk
-
-clean-local:
- rm -f $(sbin_PROGRAMS)
-
-FENCE_TEST_ARGS = -n test -a test -p test -u test
-
-include $(top_srcdir)/make/agentccheck.mk
-
-# we do not test fence_zvm because it can be compiled only on specific architecture
-check: xml-check.fence_zvmip delay-check.fence_zvmip
\ No newline at end of file
+include $(top_srcdir)/make/fencebuild.mk
+include $(top_srcdir)/make/fenceman.mk
+include $(top_srcdir)/make/agentpycheck.mk
diff --git a/fence/agents/zvm/fence_zvmip.8 b/fence/agents/zvm/fence_zvmip.8
deleted file mode 100644
index 6b01425..0000000
--- a/fence/agents/zvm/fence_zvmip.8
+++ /dev/null
@@ -1,92 +0,0 @@
-.TH fence_zvmip 8
-
-.SH NAME
-fence_zvmip - Power Fencing agent for GFS on System z z/VM Clusters using IP interface to
SMAPI
-
-.SH SYNOPSIS
-.B
-fence_zvmip
-[\fIOPTION\fR]...
-
-.SH DESCRIPTION
-fence_zvmip is a Power Fencing agent used on a GFS virtual machine in a System z z/VM
cluster.
-It uses the TCP/IP SMAPI interface to recycle an active image.
-
-fence_zvmip accepts options on the command line as well as from stdin.
-fence_node sends the options through stdin when it execs the agent.
-fence_zvmip can be run by itself with command line options which is useful
-for testing.
-
-Vendor URL:
http://www.sinenomine.net
-
-.SH OPTIONS
-.TP
-\fB-o --action\fP
-Fencing action: "off" - deactivate virtual machine; "on" - activate
virtual machine; "metadata" - display device metadata" - describe fence
agent parameters; "status" - state of virtual machine
-.TP
-\fB--delay\fP \fIseconds\fP
-Time to delay fencing action in seconds
-.TP
-\fB-n --plug\fP \fItarget\fP
-Name of target virtual machine to fence
-.TP
-\fB-h --help\fP
-Print out a help message describing available options, then exit.
-.TP
-\fB-a --ip\fP \fIsmapi Server\fP
-Host name or IP address of SMAPI server
-.TP
-\fB-u --username\fP \fISMAPI authorized user\fP
-Name of an authorized SMAPI user
-.TP
-\fB-p --password\fP \fISMAPI authorized user's password\fP
-Password of the authorized SMAPI user
-.TP
-\fB-t --timeout\fP \fIRecycle timeout\fP
-Amount of \fIgrace\fP time to give the virtual machine to shutdown cleanly before being
-forcibly terminated. Currently this is ignored.
-.TP
-\fB-h --help\fP
-Display usage information
-
-.SH STDIN PARAMETERS
-.TP
-\fIagent = < param >\fP
-This option is used by fence_node(8) and is ignored by fence_zvmip.
-.TP
-\fIaction = < action >\fP
-Fencing action: "off" - deactivate virtual machine; "on" - activate
virtual machine; "metadata" - display device metadata" - describe fence
agent parameters; "status" - state of virtual machine
-.TP
-\fIplug = < plug >\fP
-Name of virtual machine to recycle.
-.TP
-\fIipaddr = < server host name or IP address >\fP
-Host name or IP address of SMAPI server
-.TP
-\fIlogin = < SMAPI authorized user >\fP
-Name of an authorized SMAPI user
-.TP
-\fIpasswd = < SMAPI authorized user's password >\fP
-Password of the authorized SMAPI user
-.TP
-\fItimeout = < shutdown timeout >\fP
-Amount of \fIgrace\fP time to give the virtual machine to shutdown cleanly before being
-forcibly terminated. Currently this is ignored.
-
-.SH SEE ALSO
-fence(8), fenced(8), fence_node(8)
-
-.SH NOTES
-To use this agent the z/VM SMAPI service needs to be configured to allow the virtual
-machine running this agent to connect to it and issue the image_recycle operation.
-This involves updating the VSMWORK1 AUTHLIST VMSYS:VSMWORK1. file. The entry should look
-something similar to this:
-
-.nf
-Column 1 Column 66 Column 131
-| | |
-V V V
-XXXXXXXX ALL IMAGE_OPERATIONS
-.fi
-
-Where XXXXXXX is the name of the virtual machine used in the authuser field of the
request.
diff --git a/fence/agents/zvm/fence_zvmip.py b/fence/agents/zvm/fence_zvmip.py
new file mode 100644
index 0000000..63f7fa7
--- /dev/null
+++ b/fence/agents/zvm/fence_zvmip.py
@@ -0,0 +1,172 @@
+#!/usr/bin/python -tt
+
+import sys
+import atexit
+import socket
+import struct
+import logging
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, fail_usage, run_delay, EC_LOGIN_DENIED, EC_TIMED_OUT
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION=""
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
+#END_VERSION_GENERATION
+
+INT4 = 4
+
+def open_socket(options):
+ try:
+ if "--inet6-only" in options:
+ protocol = socket.AF_INET6
+ elif "--inet4-only" in options:
+ protocol = socket.AF_INET
+ else:
+ protocol = 0
+ (_, _, _, _, addr) = socket.getaddrinfo( \
+ options["--ip"], options["--ipport"], protocol,
+ 0, socket.IPPROTO_TCP, socket.AI_PASSIVE
+ )[0]
+ except socket.gaierror:
+ fail(EC_LOGIN_DENIED)
+
+ conn = socket.socket()
+ conn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ conn.settimeout(float(options["--shell-timeout"]))
+ try:
+ conn.connect(addr)
+ except socket.error:
+ fail(EC_LOGIN_DENIED)
+
+ return conn
+
+def smapi_pack_string(string):
+ return struct.pack("!i%ds" % (len(string)), len(string), string)
+
+def prepare_smapi_command(options, smapi_function, additional_args):
+ packet_size = 3*INT4 + len(smapi_function) + len(options["--username"]) +
len(options["--password"])
+ for arg in additional_args:
+ packet_size += INT4 + len(arg)
+
+ command = struct.pack("!i", packet_size)
+ command += smapi_pack_string(smapi_function)
+ command += smapi_pack_string(options["--username"])
+ command += smapi_pack_string(options["--password"])
+ for arg in additional_args:
+ command += smapi_pack_string(arg)
+
+ return command
+
+def get_power_status(conn, options):
+ del conn
+
+ # '*' = list all active images
+ (return_code, reason_code, images_active) = \
+ get_list_of_images(options, "Image_Status_Query", "*")
+ logging.debug("Image_Status_Query results are (%d,%d)", return_code,
reason_code)
+ (return_code, reason_code, images_defined) = \
+ get_list_of_images(options, "Image_Name_Query_DM",
options["--username"])
+ logging.debug("Image_Name_Query_DM results are (%d,%d)", return_code,
reason_code)
+
+ if ["list", "monitor"].count(options["--action"]) == 1:
+ return dict([(i, ("", "on" if i in images_active else
"off")) for i in images_defined])
+ else:
+ status = "error"
+ if options["--plug"].upper() in images_defined:
+ if options["--plug"].upper() in images_active:
+ status = "on"
+ else:
+ status = "off"
+ return status
+
+def set_power_status(conn, options):
+ conn = open_socket(options)
+
+ packet = None
+ if options["--action"] == "on":
+ packet = prepare_smapi_command(options, "Image_Activate",
[options["--plug"]])
+ elif options["--action"] == "off":
+ packet = prepare_smapi_command(options, "Image_Deactivate",
[options["--plug"], "IMMED"])
+ conn.send(packet)
+
+ request_id = struct.unpack("!i", conn.recv(INT4))[0]
+ (output_len, request_id, return_code, reason_code) = struct.unpack("!iiii",
conn.recv(INT4 * 4))
+ logging.debug("Image_(De)Activate results are (%d,%d)", return_code,
reason_code)
+
+ conn.close()
+ return
+
+def get_list_of_images(options, command, data_as_plug):
+ conn = open_socket(options)
+
+ packet = prepare_smapi_command(options, command, [data_as_plug])
+ conn.send(packet)
+
+ request_id = struct.unpack("!i", conn.recv(INT4))[0]
+ (output_len, request_id, return_code, reason_code) = struct.unpack("!iiii",
conn.recv(INT4 * 4))
+ images = set()
+
+ if output_len > 3*INT4:
+ array_len = struct.unpack("!i", conn.recv(INT4))[0]
+ data = ""
+
+ while True:
+ read_data = conn.recv(1024, socket.MSG_WAITALL)
+ data += read_data
+ if array_len == len(data):
+ break
+ elif not read_data:
+ logging.error("Failed: Not enough data read from socket")
+ fail(EC_TIMED_OUT)
+
+ parsed_len = 0
+ while parsed_len < array_len:
+ string_len = struct.unpack("!i", data[parsed_len:parsed_len+INT4])[0]
+ parsed_len += INT4
+ image_name = struct.unpack("!%ds" % (string_len),
data[parsed_len:parsed_len+string_len])[0]
+ parsed_len += string_len
+ images.add(image_name)
+
+ conn.close()
+ return (return_code, reason_code, images)
+
+def main():
+ device_opt = ["ipaddr", "login", "passwd",
"port", "method"]
+
+ atexit.register(atexit_handler)
+
+ all_opt["ipport"]["default"] = "44444"
+ all_opt["shell_timeout"]["default"] = "5.0"
+ options = check_input(device_opt, process_input(device_opt))
+
+ if len(options.get("--plug", "")) > 8:
+ fail_usage("Failed: Name of image can not be longer than 8 characters")
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for use with z/VM Virtual
Machines"
+ docs["longdesc"] = """The fence_zvm agent is intended to be
used with with z/VM SMAPI service via TCP/IP
+
+To use this agent the z/VM SMAPI service needs to be configured to allow the virtual
machine running this agent to connect to it and issue
+the image_recycle operation. This involves updating the VSMWORK1 AUTHLIST
VMSYS:VSMWORK1. file. The entry should look something similar to
+this:
+
+Column 1 Column 66 Column 131
+
+ | | |
+ V V V
+
+XXXXXXXX ALL IMAGE_OPERATIONS
+
+Where XXXXXXX is the name of the virtual machine used in the authuser field of the
request.
+"""
+ docs["vendorurl"] = "http://www.ibm.com"
+ show_docs(options, docs)
+
+ run_delay(options)
+ result = fence_action(None, options, set_power_status, get_power_status,
get_power_status)
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/data/metadata/fence_zvmip.xml b/tests/data/metadata/fence_zvmip.xml
index 7791303..75d90c4 100644
--- a/tests/data/metadata/fence_zvmip.xml
+++ b/tests/data/metadata/fence_zvmip.xml
@@ -1,51 +1,138 @@
<?xml version="1.0" ?>
-<resource-agent name="fence_zvmip" shortdesc="Fence agent for use with
z/VM Virtual Machines">
-<longdesc>The fence_zvm agent is intended to be used with with z/VM SMAPI service
via TCP/IP</longdesc>
+<resource-agent name="fence_zvmip" shortdesc="Fence agent for use with
z/VM Virtual Machines" >
+<longdesc>The fence_zvm agent is intended to be used with with z/VM SMAPI service
via TCP/IP
+
+To use this agent the z/VM SMAPI service needs to be configured to allow the virtual
machine running this agent to connect to it and issue
+the image_recycle operation. This involves updating the VSMWORK1 AUTHLIST
VMSYS:VSMWORK1. file. The entry should look something similar to
+this:
+
+Column 1 Column 66 Column 131
+
+ | | |
+ V V V
+
+XXXXXXXX ALL IMAGE_OPERATIONS
+
+Where XXXXXXX is the name of the virtual machine used in the authuser field of the
request.
+</longdesc>
<vendor-url>http://www.ibm.com</vendor-url>
<parameters>
- <parameter name="port" unique="1" required="1">
- <getopt mixed="-n, --plug" />
- <content type="string" />
- <shortdesc lang="en">Name of the Virtual Machine to be
fenced</shortdesc>
- </parameter>
- <parameter name="ipaddr" unique="1" required="1">
- <getopt mixed="-i, --ip" />
- <content type="string" />
- <shortdesc lang="en">IP Name or Address of SMAPI
Server</shortdesc>
- </parameter>
- <parameter name="login" unique="1" required="1">
- <getopt mixed="-u, --username" />
- <content type="string" />
- <shortdesc lang="en">Name of authorized SMAPI user</shortdesc>
- </parameter>
- <parameter name="passwd" unique="1" required="1">
- <getopt mixed="-p, --password" />
- <content type="string" />
- <shortdesc lang="en">Password of authorized SMAPI
user</shortdesc>
- </parameter>
- <parameter name="action" unique="1" required="0">
- <getopt mixed="-o, --action" />
- <content type="string" default="off" />
- <shortdesc lang="en">Fencing action</shortdesc>
- </parameter>
- <parameter name="delay" unique="1" required="0">
- <getopt mixed="--delay" />
- <content type="string" default="0" />
- <shortdesc lang="en">Time to delay fencing action in
seconds</shortdesc>
- </parameter>
- <parameter name="usage" unique="1" required="0">
+ <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="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="ipaddr" unique="0" required="1">
+ <getopt mixed="-a, --ip=[ip]" />
+ <content type="string" />
+ <shortdesc lang="en">IP Address or Hostname</shortdesc>
+ </parameter>
+ <parameter name="ipport" unique="0" required="0">
+ <getopt mixed="-u, --ipport=[port]" />
+ <content type="string" default="44444" />
+ <shortdesc lang="en">TCP/UDP port to use for connection with
device</shortdesc>
+ </parameter>
+ <parameter name="login" unique="0" required="1">
+ <getopt mixed="-l, --username=[name]" />
+ <content type="string" />
+ <shortdesc lang="en">Login Name</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="passwd" unique="0" required="0">
+ <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">
+ <getopt mixed="-S, --password-script=[script]" />
+ <content type="string" />
+ <shortdesc lang="en">Script to retrieve password</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="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">Print usage</shortdesc>
+ <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="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="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="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="shell_timeout" unique="0"
required="0">
+ <getopt mixed="--shell-timeout=[seconds]" />
+ <content type="string" default="5.0" />
+ <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="on" automatic="0" />
+ <action name="reboot" />
+ <action name="status" />
<action name="list" />
- <action name="metadata" />
<action name="monitor" />
- <action name="status" />
- <action name="reboot" />
+ <action name="metadata" />
</actions>
</resource-agent>