summaryrefslogtreecommitdiffstats
path: root/tests/fence_testing.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtests/fence_testing.py126
1 files changed, 126 insertions, 0 deletions
diff --git a/tests/fence_testing.py b/tests/fence_testing.py
new file mode 100755
index 0000000..14cfe0c
--- /dev/null
+++ b/tests/fence_testing.py
@@ -0,0 +1,126 @@
+""" Library for fence agents testing via predefined scenarios """
+from configobj import ConfigObj
+import re, sys, os
+
+EC_CONFIG_FAIL = 1
+
+def _prepare_command(agent_file, method):
+ """ Parse configuration of fence device and prepare (command + STDIN values) to execute.
+
+ Fence device configuration is used to generate a command which can be executed.
+ Because fence agents supports several options how to enter data, we can select
+ from three different methods ("stdin", "getopt" - short options, "longopt").
+ When method "stdin" is used then this function will generate also text which should
+ be entered on STDIN instead of command itself.
+
+ Example of agent definition:
+ name = "Dummy fence device configuration"
+ agent = "/bin/true"
+ [options]
+ login = [ "foo", "--username", "-l" ]
+ passwd = [ "bar", "--password", "-p" ]
+ ipaddr = [ "fence.example.com", "--ip", "-a" ]
+ port = [ "1", "--plug" ]
+ """
+ assert (method in ["stdin", "getopt", "longopt"]), "Invalid method entered"
+
+ config = ConfigObj(agent_file, unrepr = True)
+
+ assert ("agent" in config), "Fence agent has to be defined"
+ final_command = config["agent"]
+ stdin_values = None
+
+ for opt in list(config["options"].keys()):
+ assert isinstance(config["options"][opt], list), "Option %s have to have at least value and longopt"% (opt)
+ assert len(config["options"][opt]) >= 2, "Option %s have to have at least value and longopt"% (opt)
+ value = config["options"][opt][0]
+ if opt == "action":
+ ## ignore action as it is not part of fence device definition
+ continue
+
+ if method == "stdin":
+ option = opt
+ if stdin_values == None:
+ stdin_values = ""
+ stdin_values += option + "=" + value + "\n"
+ elif method == "longopt":
+ option = config["options"][opt][1]
+ final_command += " " + option + " " + value
+ elif method == "getopt":
+ if len(config["options"][opt]) == (2 + 1):
+ option = config["options"][opt][2]
+ else:
+ option = config["options"][opt][1]
+ final_command += " " + option + " " + value
+
+ return (final_command, stdin_values)
+
+def test_action(agent, action_file, method, verbose = False):
+ """ Run defined sequence of actions on a given fence agent.
+
+ This function will run one set of test on a fence agent. Test itself consists of
+ sequence of action and expected return codes. User can select from actions supported
+ by fence agent (on, off, reboot, list, status, monitor) and sleep(X) command where X
+ is in seconds and determine the length of pause between commands. Each action has to
+ have defined regular expression which define acceptable return codes from fence agent
+ or sleep.
+
+ Example of action configuration file:
+ name = "Simple Status"
+ actions = [ { "command" : "status", "return_code" : "^[02]$" }, { "command" : "sleep(1)", "return_code" : "^0$" } ]
+ """
+ re_sleep_command = re.compile('sleep\(([0-9]+)\)', re.IGNORECASE)
+ config = ConfigObj(action_file, unrepr = True)
+
+ (command, stdin_options) = _prepare_command(agent, method)
+
+ for action in config["actions"]:
+ assert "command" in action, "Action %s need to have defined 'command'"% (action_file)
+ assert "return_code" in action, "Command %s (in %s) need to have 'return_code' defined"% (action_file, action["command"])
+
+ sleep_wait = None
+ current_command = None
+ current_stdin_options = None
+
+ if not (action["command"] in [ "status", "reboot", "on", "off", "list", "monitor" ]):
+ is_sleep = re.search(re_sleep_command, action["command"])
+ if is_sleep != None:
+ sleep_wait = is_sleep.group(1)
+ else:
+ sys.stderr.write("ERROR: %s contains unsupported action \"%s\"\n"% (action_file, action["command"]))
+ sys.exit(1)
+
+ if sleep_wait != None:
+ current_command = "/bin/sleep " + sleep_wait
+ current_stdin_options = None
+ else:
+ current_command = command
+ current_stdin_options = stdin_options
+
+ if method == "stdin":
+ if current_stdin_options == None:
+ current_stdin_options = ""
+ current_stdin_options += "action=%s"% (action["command"])
+ elif method == "longopt":
+ current_command += " --action=%s"% (action["command"])
+ elif method == "getopt":
+ current_command += " -o %s"% (action["command"])
+
+ # @note: Broken pipe can occur here and I'm not sure why - non-deterministic
+ if method == "stdin" and sleep_wait == None:
+ current_command = "printf \"" + current_stdin_options + "\" | " + current_command
+
+ if verbose == False:
+ result = os.system(current_command + " &> /dev/null")
+ else:
+ print(current_command)
+ result = os.system(current_command)
+ exitcode = (result >> 8) & 0xFF
+
+ is_valid_result_code = re.search(action["return_code"], str(exitcode), re.IGNORECASE)
+
+ if is_valid_result_code == None:
+ print(("TEST FAILED: %s failed on %s when using (%s)\n"% (agent, action_file, method)))
+ print(("TEST INFO: %s returns %s\n"% (action["command"], str(exitcode))))
+ return
+ print(("TEST PASSED: %s worked on %s (%s)\n"% (agent, action_file, method)))