summaryrefslogtreecommitdiffstats
path: root/agents/autodetect
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:50:17 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:50:17 +0000
commit86ed03f8adee56c050c73018537371c230a664a6 (patch)
treeeae3d04cdf1c49848e5a671327ab38297f4acb0d /agents/autodetect
parentInitial commit. (diff)
downloadfence-agents-86ed03f8adee56c050c73018537371c230a664a6.tar.xz
fence-agents-86ed03f8adee56c050c73018537371c230a664a6.zip
Adding upstream version 4.12.1.upstream/4.12.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'agents/autodetect')
-rw-r--r--agents/autodetect/a.py8
-rwxr-xr-xagents/autodetect/autodetect.py255
-rwxr-xr-xagents/autodetect/autodetect_test.py33
-rw-r--r--agents/autodetect/b.py2
-rw-r--r--agents/autodetect/fence_apc.py259
-rw-r--r--agents/autodetect/fence_bladecenter.py111
-rw-r--r--agents/autodetect/fence_brocade.py78
-rw-r--r--agents/autodetect/fence_ilo_moonshot.py69
-rw-r--r--agents/autodetect/fence_lpar.py159
-rw-r--r--agents/autodetect/fencing.py1393
10 files changed, 2367 insertions, 0 deletions
diff --git a/agents/autodetect/a.py b/agents/autodetect/a.py
new file mode 100644
index 0000000..608d0f8
--- /dev/null
+++ b/agents/autodetect/a.py
@@ -0,0 +1,8 @@
+from b import myf
+
+def maf():
+ return 5
+
+def maf2():
+ return myf()
+ \ No newline at end of file
diff --git a/agents/autodetect/autodetect.py b/agents/autodetect/autodetect.py
new file mode 100755
index 0000000..24d9a73
--- /dev/null
+++ b/agents/autodetect/autodetect.py
@@ -0,0 +1,255 @@
+#!/usr/bin/python
+
+import pexpect
+import re
+import logging
+import time
+import sys
+import fencing
+
+import fence_apc
+import fence_bladecenter
+import fence_brocade
+import fence_rsa
+
+def check_agent(conn, options, found_prompt, prompts, test_fn, eol=None):
+ options["--action"] = "list"
+ options["--command-prompt"] = found_prompt
+ if any(x in options["--command-prompt"][0] for x in prompts):
+ options["--command-prompt"] = prompts
+
+ return test_fn(conn, options)
+ return False
+
+def get_list(conn, options, found_prompt, prompts, list_fn, eol=None):
+ def test_fn(conn, options):
+ if len(list_fn(conn, options)) > 0:
+ return True
+ else:
+ return False
+
+ return check_agent(conn, options, found_prompt, prompts, test_fn, eol)
+
+""" *************************** MAIN ******************************** """
+
+
+def detect_login_telnet(options):
+ options["--ipport"] = 23
+ re_login_string = r"([\r\n])((?!Last )login\s*:)|((?!Last )Login Name: )|(username: )|(User Name :)"
+ re_login = re.compile(re_login_string, re.IGNORECASE)
+ re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE)
+
+ options["eol"] = "\r\n"
+ conn = fencing.fspawn(options, options["--telnet-path"])
+ conn.send("set binary\n")
+ conn.send("open %s -%s\n"%(options["--ip"], options["--ipport"]))
+
+ conn.log_expect(re_login, int(options["--login-timeout"]))
+ conn.send_eol(options["--username"])
+
+ ## automatically change end of line separator
+ screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"]))
+ if re_login.search(screen) != None:
+ options["eol"] = "\n"
+ conn.send_eol(options["--username"])
+ conn.log_expect(re_pass, int(options["--login-timeout"]))
+ elif re_pass.search(screen) == None:
+ conn.log_expect(re_pass, int(options["--shell-timeout"]))
+
+ try:
+ conn.send_eol(options["--password"])
+ valid_password = conn.log_expect([re_login] + \
+ [pexpect.TIMEOUT], int(options["--shell-timeout"]))
+ if valid_password == 0:
+ ## password is invalid or we have to change EOL separator
+ options["eol"] = "\r"
+ conn.send_eol("")
+ screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"]))
+ ## after sending EOL the fence device can either show 'Login' or 'Password'
+ if re_login.search(conn.after + screen) != None:
+ conn.send_eol("")
+ conn.send_eol(options["--username"])
+ conn.log_expect(re_pass, int(options["--login-timeout"]))
+ conn.send_eol(options["--password"])
+ conn.log_expect(pexpect.TIMEOUT, int(options["--login-timeout"]))
+ except KeyError:
+ fencing.fail(fencing.EC_PASSWORD_MISSING)
+
+ found_cmd_prompt = guess_prompt(conn, options, conn.before)
+ return (found_cmd_prompt, conn)
+
+def guess_prompt(conn, options, before=""):
+ time.sleep(2)
+ conn.send_eol("")
+ conn.send_eol("")
+
+ conn.log_expect(pexpect.TIMEOUT, int(options["--login-timeout"]))
+ lines = re.split(r'\r|\n', before + conn.before)
+ logging.info("Cmd-prompt candidate: %s" % (lines[-1]))
+ if lines.count(lines[-1]) >= 3:
+ found_cmd_prompt = ["\n" + lines[-1]]
+ else:
+ if lines.count(lines[-1]) == 2:
+ conn.log_expect(lines[-1], int(options["--shell-timeout"]))
+ conn.log_expect(lines[-1], int(options["--shell-timeout"]))
+ options["eol"] = "\r"
+ conn.send_eol("")
+ time.sleep(0.1)
+ conn.send_eol("")
+ time.sleep(0.1)
+ conn.send_eol("")
+ time.sleep(0.1)
+ conn.log_expect(pexpect.TIMEOUT, int(options["--login-timeout"]))
+ lines = re.split(r'\r|\n', conn.before)
+ logging.info("Cmd-prompt candidate: %s" % (lines[1]))
+ if lines.count(lines[-1]) >= 3:
+ found_cmd_prompt = ["\n" + lines[-1]]
+ else:
+ print "Unable to obtain command prompt automatically"
+ sys.exit(1)
+ else:
+ print "Unable to obtain command prompt automatically"
+ print lines[-1]
+ print conn.before
+ sys.exit(1)
+
+ conn.log_expect(found_cmd_prompt, int(options["--shell-timeout"]))
+ conn.log_expect(found_cmd_prompt, int(options["--shell-timeout"]))
+ conn.log_expect(found_cmd_prompt, int(options["--shell-timeout"]))
+
+ # Handle situation when CR/LF is interpreted as ENTER, ENTER
+ # In such case we will have get two additional command prompts to get on right position
+ res = conn.log_expect([pexpect.TIMEOUT] + found_cmd_prompt, int(options["--shell-timeout"]))
+ if res > 0:
+ # @note: store that information?
+ print "CMD twice"
+ conn.log_expect(found_cmd_prompt, int(options["--shell-timeout"]))
+ return found_cmd_prompt
+
+def detect_login_ssh(options, version=2):
+ options["--ipport"] = 22
+ if version == "1":
+ command = '%s %s@%s -p %s -1 -c blowfish -o PubkeyAuthentication=no' % (options["--ssh-path"], options["--username"], options["--ip"], options["--ipport"])
+ else:
+ command = '%s %s@%s -p %s -o PubkeyAuthentication=no' % (options["--ssh-path"], options["--username"], options["--ip"], options["--ipport"])
+
+ conn = fencing.fspawn(options, command)
+ result = conn.log_expect(["ssword:", "Are you sure you want to continue connecting (yes/no)?"], int(options["--login-timeout"]))
+ if result == 1:
+ conn.send("yes\n")
+ conn.log_expect("ssword:", int(options["--login-timeout"]))
+
+ conn.send(options["--password"] + "\n")
+
+ found_cmd_prompt = guess_prompt(conn, options, conn.before)
+ return (found_cmd_prompt, conn)
+
+def detect_device(conn, options, found_cmd_prompt):
+ if get_list(conn, options, found_cmd_prompt, prompts=["\n>", "\napc>"], list_fn=fence_apc.get_power_status):
+ fencing.fence_logout(conn, "4")
+ return "fence_apc # older serie"
+
+ if get_list(conn, options, found_cmd_prompt, prompts=["\n>", "\napc>"], list_fn=fence_apc.get_power_status5):
+ fencing.fence_logout(conn, "exit")
+ return "fence_apc # v5+"
+
+ ## Test fence_lpar with list action (HMC version 3 and 4)
+ def test_lpar(conn, options):
+ conn.send_eol("lssyscfg; echo $?")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+ if "\n0\r\n" in conn.before:
+ return True
+ else:
+ return False
+
+ if check_agent(conn, options, found_cmd_prompt, [r":~>", r"]\$", r"\$ "], test_lpar):
+ fencing.fence_logout(conn, "quit")
+ return "fence_lpar # 2"
+
+ if get_list(conn, options, found_cmd_prompt, prompts=["system>"], list_fn=fence_bladecenter.get_blades_list):
+ fencing.fence_logout(conn, "exit")
+ return "fence_bladecenter #2"
+
+ if get_list(conn, options, found_cmd_prompt, prompts=["> "], list_fn=fence_brocade.get_power_status, eol="\n"):
+ fencing.fence_logout(conn, "exit")
+ return "fence_brocade #2"
+
+ if get_list(conn, options, found_cmd_prompt, prompts=["> "], list_fn=fence_rsa.get_power_status):
+ fencing.fence_logout(conn, "exit")
+ return "fence_rsa"
+
+ return None
+
+# Test fence ilo moonshot
+#cmd_possible = ["MP>", "hpiLO->"]
+#options["--action"] = "list"
+#options["--command-prompt"] = found_cmd_prompt
+#options["eol"] = "\n"
+#if any(x in options["--command-prompt"][0] for x in cmd_possible):
+# options["--command-prompt"] = cmd_possible
+#
+# plugs = fence_ilo_moonshot.get_power_status(conn, options)
+# if len(plugs) > 0:
+# print "fence_ilo_moonshot # "
+# fencing.fence_logout(conn, "exit")
+# sys.exit(0)
+
+def xxx():
+ ## login mechanism as in fencing.py.py - differences is that we do not know command prompt
+ #logging.getLogger().setLevel(logging.DEBUG)
+ options = {}
+ options["--ssh-path"] = "/usr/bin/ssh"
+ options["--telnet-path"] = "/usr/bin/telnet"
+
+ # virtual machine
+ #options["--username"] = "marx"
+ #options["--ip"] = "localhost"
+ #options["--password"] = "batalion"
+
+ # APC
+ #options["--username"] = "labuser"
+ #options["--ip"] = "pdu-bar.englab.brq.redhat.com"
+ #options["--password"] = "labuser"
+
+ # LPAR
+ options["--username"] = "rhts"
+ options["--ip"] = "ppc-hmc-01.mgmt.lab.eng.bos.redhat.com"
+ #options["--ip"] = "ibm-js22-vios-02.rhts.eng.bos.redhat.com"
+ options["--password"] = "100yard-"
+
+ # Bladecenter
+ options["--ip"] = "blade-mm.englab.brq.redhat.com"
+
+ # Brocade
+ #options["--ip"] = "hp-fcswitch-01.lab.bos.redhat.com"
+ #options["--password"] = "password"
+ #options["--username"] = "admin"
+
+ # iLO Moonshot - chova sa to divne
+ #options["--password"] = "Access@gis"
+ #options["--username"] = "rcuser"
+ #options["--ip"] = "hp-m1500-mgmt.gsslab.pnq.redhat.com"
+
+ #options["--ip"] = "ibm-x3755-01-rsa.ovirt.rhts.eng.bos.redhat.com"
+ #options["--username"] = "USERID"
+ #options["--password"] = "PASSW0RD"
+
+ options["--login-timeout"] = "10"
+ options["--shell-timeout"] = "5"
+ options["--power-timeout"] = "10"
+
+ options["eol"] = "\r\n"
+
+ (found_cmd_prompt, conn) = detect_login_telnet(options)
+ #(found_cmd_prompt, conn) = detect_login_ssh(options)
+
+ res = detect_device(conn, options, found_cmd_prompt)
+ if not res is None:
+ print res
+ sys.exit(0)
+ else:
+ ## Nothing found
+ sys.exit(2)
+
+if __name__ == "__main__":
+ xxx()
diff --git a/agents/autodetect/autodetect_test.py b/agents/autodetect/autodetect_test.py
new file mode 100755
index 0000000..a18aaed
--- /dev/null
+++ b/agents/autodetect/autodetect_test.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+import unittest
+import autodetect as detect
+
+class TestDetectDevice(unittest.TestCase):
+ options = {}
+
+ def setUp(self):
+ self.options = {}
+ self.options["--ssh-path"] = "/usr/bin/ssh"
+ self.options["--telnet-path"] = "/usr/bin/telnet"
+ self.options["--login-timeout"] = "10"
+ self.options["--shell-timeout"] = "5"
+ self.options["--power-timeout"] = "10"
+ self.options["eol"] = "\r\n"
+
+ def test_bladecenter(self):
+ self.options["--username"] = "rhts"
+ self.options["--password"] = "100yard-"
+ self.options["--ip"] = "blade-mm.englab.brq.redhat.com"
+
+ (found_cmd_prompt, conn) = detect.detect_login_telnet(self.options)
+ res = detect.detect_device(conn, self.options, found_cmd_prompt)
+ self.assertEqual('fence_bladecenter', res)
+
+ def test_apc5(self):
+ self.assertEqual('foo', 'foo')
+ self.options["c"] = "c"
+ print self.options
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/agents/autodetect/b.py b/agents/autodetect/b.py
new file mode 100644
index 0000000..fdfda59
--- /dev/null
+++ b/agents/autodetect/b.py
@@ -0,0 +1,2 @@
+def myf():
+ return 3
diff --git a/agents/autodetect/fence_apc.py b/agents/autodetect/fence_apc.py
new file mode 100644
index 0000000..c6dd106
--- /dev/null
+++ b/agents/autodetect/fence_apc.py
@@ -0,0 +1,259 @@
+#!/usr/bin/python -tt
+
+#####
+##
+## The Following Agent Has Been Tested On:
+##
+## Model Firmware
+## +---------------------------------------------+
+## AP7951 AOS v2.7.0, PDU APP v2.7.3
+## AP7941 AOS v3.5.7, PDU APP v3.5.6
+## AP9606 AOS v2.5.4, PDU APP v2.7.3
+##
+## @note: ssh is very slow on AP79XX devices protocol (1) and
+## cipher (des/blowfish) have to be defined
+#####
+
+import sys, re
+import atexit
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, fail_usage, EC_STATUS
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION="New APC Agent - test release on steroids"
+REDHAT_COPYRIGHT=""
+BUILD_DATE="March, 2008"
+#END_VERSION_GENERATION
+
+def get_power_status(conn, options):
+ exp_result = 0
+ outlets = {}
+
+ conn.send_eol("1")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ version = 0
+ admin = 0
+ switch = 0
+
+ if None != re.compile('.* MasterSwitch plus.*', re.IGNORECASE | re.S).match(conn.before):
+ switch = 1
+ if None != re.compile('.* MasterSwitch plus 2', re.IGNORECASE | re.S).match(conn.before):
+ if not options.has_key("--switch"):
+ fail_usage("Failed: You have to enter physical switch number")
+ else:
+ if not options.has_key("--switch"):
+ options["--switch"] = "1"
+
+ if None == re.compile('.*Outlet Management.*', re.IGNORECASE | re.S).match(conn.before):
+ version = 2
+ else:
+ version = 3
+
+ if None == re.compile('.*Outlet Control/Configuration.*', re.IGNORECASE | re.S).match(conn.before):
+ admin = 0
+ else:
+ admin = 1
+
+ if switch == 0:
+ if version == 2:
+ if admin == 0:
+ conn.send_eol("2")
+ else:
+ conn.send_eol("3")
+ else:
+ conn.send_eol("2")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+ conn.send_eol("1")
+ else:
+ conn.send_eol(options["--switch"])
+
+ while True:
+ exp_result = conn.log_expect(
+ ["Press <ENTER>"] + options["--command-prompt"], int(options["--shell-timeout"]))
+ lines = conn.before.split("\n")
+ show_re = re.compile(r'(^|\x0D)\s*(\d+)- (.*?)\s+(ON|OFF)\s*')
+ for line in lines:
+ res = show_re.search(line)
+ if res != None:
+ outlets[res.group(2)] = (res.group(3), res.group(4))
+ conn.send_eol("")
+ if exp_result != 0:
+ break
+ conn.send(chr(03))
+ conn.log_expect("- Logout", int(options["--shell-timeout"]))
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ if ["list", "monitor"].count(options["--action"]) == 1:
+ return outlets
+ else:
+ try:
+ (_, status) = outlets[options["--plug"]]
+ return status.lower().strip()
+ except KeyError:
+ fail(EC_STATUS)
+
+def set_power_status(conn, options):
+ action = {
+ 'on' : "1",
+ 'off': "2"
+ }[options["--action"]]
+
+ conn.send_eol("1")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ version = 0
+ admin2 = 0
+ admin3 = 0
+ switch = 0
+
+ if None != re.compile('.* MasterSwitch plus.*', re.IGNORECASE | re.S).match(conn.before):
+ switch = 1
+ ## MasterSwitch has different schema for on/off actions
+ action = {
+ 'on' : "1",
+ 'off': "3"
+ }[options["--action"]]
+ if None != re.compile('.* MasterSwitch plus 2', re.IGNORECASE | re.S).match(conn.before):
+ if not options.has_key("--switch"):
+ fail_usage("Failed: You have to enter physical switch number")
+ else:
+ if not options.has_key("--switch"):
+ options["--switch"] = 1
+
+ if None == re.compile('.*Outlet Management.*', re.IGNORECASE | re.S).match(conn.before):
+ version = 2
+ else:
+ version = 3
+
+ if None == re.compile('.*Outlet Control/Configuration.*', re.IGNORECASE | re.S).match(conn.before):
+ admin2 = 0
+ else:
+ admin2 = 1
+
+ if switch == 0:
+ if version == 2:
+ if admin2 == 0:
+ conn.send_eol("2")
+ else:
+ conn.send_eol("3")
+ else:
+ conn.send_eol("2")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+ if None == re.compile('.*2- Outlet Restriction.*', re.IGNORECASE | re.S).match(conn.before):
+ admin3 = 0
+ else:
+ admin3 = 1
+ conn.send_eol("1")
+ else:
+ conn.send_eol(options["--switch"])
+
+ while 0 == conn.log_expect(
+ ["Press <ENTER>"] + options["--command-prompt"], int(options["--shell-timeout"])):
+ conn.send_eol("")
+
+ conn.send_eol(options["--plug"]+"")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ if switch == 0:
+ if admin2 == 1:
+ conn.send_eol("1")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+ if admin3 == 1:
+ conn.send_eol("1")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+ else:
+ conn.send_eol("1")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ conn.send_eol(action)
+ conn.log_expect("Enter 'YES' to continue or <ENTER> to cancel :", int(options["--shell-timeout"]))
+ conn.send_eol("YES")
+ conn.log_expect("Press <ENTER> to continue...", int(options["--power-timeout"]))
+ conn.send_eol("")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+ conn.send(chr(03))
+ conn.log_expect("- Logout", int(options["--shell-timeout"]))
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+def get_power_status5(conn, options):
+ outlets = {}
+
+ conn.send_eol("olStatus all")
+
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+ lines = conn.before.split("\n")
+
+ show_re = re.compile(r'^\s*(\d+): (.*): (On|Off)\s*$', re.IGNORECASE)
+
+ for line in lines:
+ res = show_re.search(line)
+ if res != None:
+ outlets[res.group(1)] = (res.group(2), res.group(3))
+
+ if ["list", "monitor"].count(options["--action"]) == 1:
+ return outlets
+ else:
+ try:
+ (_, status) = outlets[options["--plug"]]
+ return status.lower().strip()
+ except KeyError:
+ fail(EC_STATUS)
+
+def set_power_status5(conn, options):
+ action = {
+ 'on' : "olOn",
+ 'off': "olOff"
+ }[options["--action"]]
+
+ conn.send_eol(action + " " + options["--plug"])
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+def main():
+ device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \
+ "port", "switch", "telnet"]
+
+ atexit.register(atexit_handler)
+
+ all_opt["cmd_prompt"]["default"] = ["\n>", "\napc>"]
+ all_opt["ssh_options"]["default"] = "-1 -c blowfish"
+
+ options = check_input(device_opt, process_input(device_opt))
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for APC over telnet/ssh"
+ docs["longdesc"] = "fence_apc is an I/O Fencing agent \
+which can be used with the APC network power switch. It logs into device \
+via telnet/ssh and reboots a specified outlet. Lengthy telnet/ssh connections \
+should be avoided while a GFS cluster is running because the connection \
+will block any necessary fencing actions."
+ docs["vendorurl"] = "http://www.apc.com"
+ show_docs(options, docs)
+
+ ## Support for --plug [switch]:[plug] notation that was used before
+ if (options.has_key("--plug") == 1) and (-1 != options["--plug"].find(":")):
+ (switch, plug) = options["--plug"].split(":", 1)
+ options["--switch"] = switch
+ options["--plug"] = plug
+
+ ##
+ ## Operate the fencing device
+ ####
+ conn = fence_login(options)
+
+ ## Detect firmware version (ASCII menu vs command-line interface)
+ ## and continue with proper action
+ ####
+ result = -1
+ firmware_version = re.compile(r'\s*v(\d)*\.').search(conn.before)
+ if (firmware_version != None) and (firmware_version.group(1) in [ "5", "6" ]):
+ result = fence_action(conn, options, set_power_status5, get_power_status5, get_power_status5)
+ else:
+ result = fence_action(conn, options, set_power_status, get_power_status, get_power_status)
+
+ fence_logout(conn, "4")
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/agents/autodetect/fence_bladecenter.py b/agents/autodetect/fence_bladecenter.py
new file mode 100644
index 0000000..d72c07f
--- /dev/null
+++ b/agents/autodetect/fence_bladecenter.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python -tt
+
+#####
+##
+## The Following Agent Has Been Tested On:
+##
+## Model Firmware
+## +--------------------+---------------------------+
+## (1) Main application BRET85K, rev 16
+## Boot ROM BRBR67D, rev 16
+## Remote Control BRRG67D, rev 16
+##
+#####
+
+import sys, re
+import atexit
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, EC_STATUS, EC_GENERIC_ERROR
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION="New Bladecenter Agent - test release on steroids"
+REDHAT_COPYRIGHT=""
+BUILD_DATE="March, 2008"
+#END_VERSION_GENERATION
+
+def get_power_status(conn, options):
+ node_cmd = r"system:blade\[" + options["--plug"] + r"\]>"
+
+ conn.send_eol("env -T system:blade[" + options["--plug"] + "]")
+ i = conn.log_expect([node_cmd, "system>"], int(options["--shell-timeout"]))
+ if i == 1:
+ ## Given blade number does not exist
+ if options.has_key("--missing-as-off"):
+ return "off"
+ else:
+ fail(EC_STATUS)
+ conn.send_eol("power -state")
+ conn.log_expect(node_cmd, int(options["--shell-timeout"]))
+ status = conn.before.splitlines()[-1]
+ conn.send_eol("env -T system")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ return status.lower().strip()
+
+def set_power_status(conn, options):
+ node_cmd = r"system:blade\[" + options["--plug"] + r"\]>"
+
+ conn.send_eol("env -T system:blade[" + options["--plug"] + "]")
+ i = conn.log_expect([node_cmd, "system>"], int(options["--shell-timeout"]))
+ if i == 1:
+ ## Given blade number does not exist
+ if options.has_key("--missing-as-off"):
+ return
+ else:
+ fail(EC_GENERIC_ERROR)
+
+ conn.send_eol("power -"+options["--action"])
+ conn.log_expect(node_cmd, int(options["--shell-timeout"]))
+ conn.send_eol("env -T system")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+def get_blades_list(conn, options):
+ outlets = {}
+
+ node_cmd = "system>"
+
+ conn.send_eol("env -T system")
+ conn.log_expect(node_cmd, int(options["--shell-timeout"]))
+ conn.send_eol("list -l 2")
+ conn.log_expect(node_cmd, int(options["--shell-timeout"]))
+
+ lines = conn.before.split("\r\n")
+ filter_re = re.compile(r"^\s*blade\[(\d+)\]\s+(.*?)\s*$")
+ for blade_line in lines:
+ res = filter_re.search(blade_line)
+ if res != None:
+ outlets[res.group(1)] = (res.group(2), "")
+
+ return outlets
+
+def main():
+ device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \
+ "port", "missing_as_off", "telnet"]
+
+ atexit.register(atexit_handler)
+
+ all_opt["power_wait"]["default"] = "10"
+ all_opt["cmd_prompt"]["default"] = ["system>"]
+
+ options = check_input(device_opt, process_input(device_opt))
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for IBM BladeCenter"
+ docs["longdesc"] = "fence_bladecenter is an I/O Fencing agent \
+which can be used with IBM Bladecenters with recent enough firmware that \
+includes telnet support. It logs into a Brocade chasis via telnet or ssh \
+and uses the command line interface to power on and off blades."
+ docs["vendorurl"] = "http://www.ibm.com"
+ show_docs(options, docs)
+
+ ##
+ ## Operate the fencing device
+ ######
+ conn = fence_login(options, "(username\s*:\s*)")
+ result = fence_action(conn, options, set_power_status, get_power_status, get_blades_list)
+ fence_logout(conn, "exit")
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/agents/autodetect/fence_brocade.py b/agents/autodetect/fence_brocade.py
new file mode 100644
index 0000000..5257bcc
--- /dev/null
+++ b/agents/autodetect/fence_brocade.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python -tt
+
+import sys, re
+import atexit
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, EC_STATUS
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION="New Brocade Agent - test release on steroids"
+REDHAT_COPYRIGHT=""
+BUILD_DATE="March, 20013"
+#END_VERSION_GENERATION
+
+def set_power_status(conn, options):
+ action = {
+ 'on' : "portCfgPersistentEnable",
+ 'off': "portCfgPersistentDisable"
+ }[options["--action"]]
+
+ conn.send_eol(action + " " + options["--plug"])
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+def get_power_status(conn, options):
+ line_re = re.compile(r'=========', re.IGNORECASE)
+ outlets = {}
+ in_index = False
+
+ conn.send_eol("switchshow")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+ for line in str(conn.before).split("\n"):
+ if line_re.search(line):
+ in_index = True
+ elif in_index and line.lstrip()[0].isdigit():
+ tokens = line.lstrip().split()
+ status = "off" if len(tokens) > 7 and tokens[7] == "Disabled" else "on"
+ outlets[tokens[0]] = ("", status)
+
+ if ["list", "monitor"].count(options["--action"]) == 0:
+ (_, status) = outlets[options["--plug"]]
+ return status
+ else:
+ return outlets
+
+def main():
+ device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \
+ "port", "fabric_fencing", "telnet"]
+
+ atexit.register(atexit_handler)
+
+ all_opt["cmd_prompt"]["default"] = ["> "]
+
+ options = check_input(device_opt, process_input(device_opt))
+ options["eol"] = "\n"
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for HP Brocade over telnet/ssh"
+ docs["longdesc"] = "fence_brocade is an I/O Fencing agent which can be used with Brocade FC switches. \
+It logs into a Brocade switch via telnet and disables a specified port. Disabling the port which a machine is \
+connected to effectively fences that machine. Lengthy telnet connections to the switch should be avoided while \
+a GFS cluster is running because the connection will block any necessary fencing actions. \
+\
+After a fence operation has taken place the fenced machine can no longer connect to the Brocade FC switch. \
+When the fenced machine is ready to be brought back into the GFS cluster (after reboot) the port on the Brocade \
+FC switch needs to be enabled. This can be done by running fence_brocade and specifying the enable action"
+ docs["vendorurl"] = "http://www.brocade.com"
+ show_docs(options, docs)
+
+ ##
+ ## Operate the fencing device
+ ####
+ conn = fence_login(options)
+ result = fence_action(conn, options, set_power_status, get_power_status, get_power_status)
+ fence_logout(conn, "exit")
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/agents/autodetect/fence_ilo_moonshot.py b/agents/autodetect/fence_ilo_moonshot.py
new file mode 100644
index 0000000..e161ac6
--- /dev/null
+++ b/agents/autodetect/fence_ilo_moonshot.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python -tt
+
+import sys
+import atexit
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, EC_STATUS
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION=""
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
+#END_VERSION_GENERATION
+
+def get_power_status(conn, options):
+ conn.send_eol("show node list")
+ conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"]))
+
+ nodes = {}
+ for line in conn.before.splitlines():
+ if len(line.split()) == 10:
+ nodes[line.split()[1]] = ("", line.split()[8].lower().strip())
+
+ if ["list", "monitor"].count(options["--action"]) == 1:
+ return nodes
+ else:
+ try:
+ (_, status) = nodes[options["--plug"]]
+ return status.lower()
+ except KeyError:
+ fail(EC_STATUS)
+
+def set_power_status(conn, options):
+ if options["--action"] == "on":
+ conn.send_eol("set node power on %s" % (options["--plug"]))
+ else:
+ conn.send_eol("set node power off force %s" % (options["--plug"]))
+
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+ return
+
+def main():
+ device_opt = ["ipaddr", "login", "passwd", "secure", "cmd_prompt", "port"]
+
+ atexit.register(atexit_handler)
+
+ all_opt["secure"]["default"] = "1"
+ all_opt["cmd_prompt"]["default"] = ["MP>", "hpiLO->"]
+
+ options = check_input(device_opt, process_input(device_opt))
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for HP Moonshot iLO"
+ docs["longdesc"] = ""
+ docs["vendorurl"] = "http://www.hp.com"
+ show_docs(options, docs)
+
+ conn = fence_login(options)
+
+ ##
+ ## Fence operations
+ ####
+ result = fence_action(conn, options, set_power_status, get_power_status, get_power_status)
+ fence_logout(conn, "exit")
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/agents/autodetect/fence_lpar.py b/agents/autodetect/fence_lpar.py
new file mode 100644
index 0000000..6676e1c
--- /dev/null
+++ b/agents/autodetect/fence_lpar.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python -tt
+
+#####
+##
+## The Following Agent Has Been Tested On:
+##
+## Version
+## +---------------------------------------------+
+## Tested on HMC
+##
+#####
+
+import sys, re
+import atexit
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from fencing import *
+from fencing import fail, fail_usage, EC_STATUS_HMC
+
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION=""
+REDHAT_COPYRIGHT=""
+BUILD_DATE=""
+#END_VERSION_GENERATION
+
+def get_power_status(conn, options):
+ if options["--hmc-version"] == "3":
+ conn.send("lssyscfg -r lpar -m " + options["--managed"] + " -n " + options["--plug"] + " -F name,state\n")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+ try:
+ status = re.compile("^" + options["--plug"] + ",(.*?),.*$",
+ re.IGNORECASE | re.MULTILINE).search(conn.before).group(1)
+ except AttributeError:
+ fail(EC_STATUS_HMC)
+ elif options["--hmc-version"] == "4":
+ conn.send("lssyscfg -r lpar -m "+ options["--managed"] +
+ " --filter 'lpar_names=" + options["--plug"] + "'\n")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+ try:
+ status = re.compile(",state=(.*?),", re.IGNORECASE).search(conn.before).group(1)
+ except AttributeError:
+ fail(EC_STATUS_HMC)
+
+ ##
+ ## Transformation to standard ON/OFF status if possible
+ if status in ["Running", "Open Firmware", "Shutting Down", "Starting"]:
+ status = "on"
+ else:
+ status = "off"
+
+ return status
+
+def set_power_status(conn, options):
+ if options["--hmc-version"] == "3":
+ conn.send("chsysstate -o " + options["--action"] + " -r lpar -m " + options["--managed"]
+ + " -n " + options["--plug"] + "\n")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+ elif options["--hmc-version"] == "4":
+ if options["--action"] == "on":
+ conn.send("chsysstate -o on -r lpar -m " + options["--managed"] +
+ " -n " + options["--plug"] +
+ " -f `lssyscfg -r lpar -F curr_profile " +
+ " -m " + options["--managed"] +
+ " --filter \"lpar_names=" + options["--plug"] + "\"`\n")
+ else:
+ conn.send("chsysstate -o shutdown -r lpar --immed" +
+ " -m " + options["--managed"] + " -n " + options["--plug"] + "\n")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+def get_lpar_list(conn, options):
+ outlets = {}
+ if options["--hmc-version"] == "3":
+ conn.send("query_partition_names -m " + options["--managed"] + "\n")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+ ## We have to remove first 3 lines (command + header) and last line (part of new prompt)
+ ####
+ res = re.search("^.+?\n(.+?\n){2}(.*)\n.*$", conn.before, re.S)
+
+ if res == None:
+ fail_usage("Unable to parse output of list command")
+
+ lines = res.group(2).split("\n")
+ for outlet_line in lines:
+ outlets[outlet_line.rstrip()] = ("", "")
+ elif options["--hmc-version"] == "4":
+ conn.send("lssyscfg -r lpar -m " + options["--managed"] +
+ " -F name:state\n")
+ conn.log_expect(options["--command-prompt"], int(options["--power-timeout"]))
+
+ ## We have to remove first line (command) and last line (part of new prompt)
+ ####
+ res = re.search("^.+?\n(.*)\n.*$", conn.before, re.S)
+
+ if res == None:
+ fail_usage("Unable to parse output of list command")
+
+ lines = res.group(1).split("\n")
+ for outlet_line in lines:
+ (port, status) = outlet_line.split(":")
+ outlets[port] = ("", status)
+
+ return outlets
+
+def define_new_opts():
+ all_opt["managed"] = {
+ "getopt" : "s:",
+ "longopt" : "managed",
+ "help" : "-s, --managed=[id] Name of the managed system",
+ "required" : "0",
+ "shortdesc" : "Managed system name",
+ "order" : 1}
+ all_opt["hmc_version"] = {
+ "getopt" : "H:",
+ "longopt" : "hmc-version",
+ "help" : "-H, --hmc-version=[version] Force HMC version to use: (3|4) (default: 4)",
+ "required" : "0",
+ "shortdesc" : "Force HMC version to use",
+ "default" : "4",
+ "choices" : ["3", "4"],
+ "order" : 1}
+
+def main():
+ device_opt = ["ipaddr", "login", "passwd", "secure", "cmd_prompt", \
+ "port", "managed", "hmc_version"]
+
+ atexit.register(atexit_handler)
+
+ define_new_opts()
+
+ all_opt["login_timeout"]["default"] = "15"
+ all_opt["secure"]["default"] = "1"
+ all_opt["cmd_prompt"]["default"] = [r":~>", r"]\$", r"\$ "]
+
+ options = check_input(device_opt, process_input(device_opt), other_conditions = True)
+
+ docs = {}
+ docs["shortdesc"] = "Fence agent for IBM LPAR"
+ docs["longdesc"] = ""
+ docs["vendorurl"] = "http://www.ibm.com"
+ show_docs(options, docs)
+
+ if not options.has_key("--managed"):
+ fail_usage("Failed: You have to enter name of managed system")
+
+ if options["--action"] == "validate-all":
+ sys.exit(0)
+
+ ##
+ ## Operate the fencing device
+ ####
+ conn = fence_login(options)
+ result = fence_action(conn, options, set_power_status, get_power_status, get_lpar_list)
+ fence_logout(conn, "quit\r\n")
+ sys.exit(result)
+
+if __name__ == "__main__":
+ main()
diff --git a/agents/autodetect/fencing.py b/agents/autodetect/fencing.py
new file mode 100644
index 0000000..ea21ace
--- /dev/null
+++ b/agents/autodetect/fencing.py
@@ -0,0 +1,1393 @@
+#!/usr/bin/python -tt
+
+import sys, getopt, time, os, uuid, pycurl, stat
+import pexpect, re, syslog
+import logging
+import subprocess
+import threading
+import shlex
+import exceptions
+import socket
+import textwrap
+import __main__
+
+## do not add code here.
+#BEGIN_VERSION_GENERATION
+RELEASE_VERSION="4.0.21.23-eaa13"
+BUILD_DATE="(built Wed Nov 4 13:28:43 CET 2015)"
+REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved."
+#END_VERSION_GENERATION
+
+__all__ = ['atexit_handler', 'check_input', 'process_input', 'all_opt', 'show_docs',
+ 'fence_login', 'fence_action', 'fence_logout']
+
+EC_OK = 0
+EC_GENERIC_ERROR = 1
+EC_BAD_ARGS = 2
+EC_LOGIN_DENIED = 3
+EC_CONNECTION_LOST = 4
+EC_TIMED_OUT = 5
+EC_WAITING_ON = 6
+EC_WAITING_OFF = 7
+EC_STATUS = 8
+EC_STATUS_HMC = 9
+EC_PASSWORD_MISSING = 10
+EC_INVALID_PRIVILEGES = 11
+
+all_opt = {
+ "help" : {
+ "getopt" : "h",
+ "longopt" : "help",
+ "help" : "-h, --help Display this help and exit",
+ "required" : "0",
+ "shortdesc" : "Display help and exit",
+ "order" : 54},
+ "version" : {
+ "getopt" : "V",
+ "longopt" : "version",
+ "help" : "-V, --version Display version information and exit",
+ "required" : "0",
+ "shortdesc" : "Display version information and exit",
+ "order" : 53},
+ "verbose" : {
+ "getopt" : "v",
+ "longopt" : "verbose",
+ "help" : "-v, --verbose Verbose mode",
+ "required" : "0",
+ "order" : 51},
+ "debug" : {
+ "getopt" : "D:",
+ "longopt" : "debug-file",
+ "help" : "-D, --debug-file=[debugfile] Debugging to output file",
+ "required" : "0",
+ "shortdesc" : "Write debug information to given file",
+ "order" : 52},
+ "delay" : {
+ "getopt" : ":",
+ "longopt" : "delay",
+ "help" : "--delay=[seconds] Wait X seconds before fencing is started",
+ "required" : "0",
+ "default" : "0",
+ "order" : 200},
+ "agent" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "web" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "force_on" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "action" : {
+ "getopt" : "o:",
+ "longopt" : "action",
+ "help" : "-o, --action=[action] Action: status, reboot (default), off or on",
+ "required" : "1",
+ "shortdesc" : "Fencing action",
+ "default" : "reboot",
+ "order" : 1},
+ "fabric_fencing" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "ipaddr" : {
+ "getopt" : "a:",
+ "longopt" : "ip",
+ "help" : "-a, --ip=[ip] IP address or hostname of fencing device",
+ "required" : "1",
+ "order" : 1},
+ "ipport" : {
+ "getopt" : "u:",
+ "longopt" : "ipport",
+ "help" : "-u, --ipport=[port] TCP/UDP port to use for connection",
+ "required" : "0",
+ "shortdesc" : "TCP/UDP port to use for connection with device",
+ "order" : 1},
+ "login" : {
+ "getopt" : "l:",
+ "longopt" : "username",
+ "help" : "-l, --username=[name] Login name",
+ "required" : "?",
+ "order" : 1},
+ "no_login" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "no_password" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "no_port" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "no_status" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "no_on" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "no_off" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "telnet" : {
+ "getopt" : "",
+ "help" : "",
+ "order" : ""},
+ "passwd" : {
+ "getopt" : "p:",
+ "longopt" : "password",
+ "help" : "-p, --password=[password] Login password or passphrase",
+ "required" : "0",
+ "order" : 1},
+ "passwd_script" : {
+ "getopt" : "S:",
+ "longopt" : "password-script",
+ "help" : "-S, --password-script=[script] Script to run to retrieve password",
+ "required" : "0",
+ "order" : 1},
+ "identity_file" : {
+ "getopt" : "k:",
+ "longopt" : "identity-file",
+ "help" : "-k, --identity-file=[filename] Identity file (private key) for SSH",
+ "required" : "0",
+ "order" : 1},
+ "cmd_prompt" : {
+ "getopt" : "c:",
+ "longopt" : "command-prompt",
+ "help" : "-c, --command-prompt=[prompt] Force Python regex for command prompt",
+ "required" : "0",
+ "order" : 1},
+ "secure" : {
+ "getopt" : "x",
+ "longopt" : "ssh",
+ "help" : "-x, --ssh Use SSH connection",
+ "required" : "0",
+ "order" : 1},
+ "ssh_options" : {
+ "getopt" : ":",
+ "longopt" : "ssh-options",
+ "help" : "--ssh-options=[options] SSH options to use",
+ "required" : "0",
+ "order" : 1},
+ "ssl" : {
+ "getopt" : "z",
+ "longopt" : "ssl",
+ "help" : "-z, --ssl Use SSL connection with verifying certificate",
+ "required" : "0",
+ "order" : 1},
+ "ssl_insecure" : {
+ "getopt" : "",
+ "longopt" : "ssl-insecure",
+ "help" : "--ssl-insecure Use SSL connection without verifying certificate",
+ "required" : "0",
+ "order" : 1},
+ "ssl_secure" : {
+ "getopt" : "",
+ "longopt" : "ssl-secure",
+ "help" : "--ssl-secure Use SSL connection with verifying certificate",
+ "required" : "0",
+ "order" : 1},
+ "notls" : {
+ "getopt" : "t",
+ "longopt" : "notls",
+ "help" : "-t, --notls "
+ "Disable TLS negotiation and force SSL3.0. "
+ "This should only be used for devices that do not support TLS1.0 and up.",
+ "required" : "0",
+ "order" : 1},
+ "tls1.0" : {
+ "getopt" : "",
+ "longopt" : "tls1.0",
+ "help" : "--tls1.0 "
+ "Disable TLS negotiation and force TLS1.0. "
+ "This should only be used for devices that do not support TLS1.1 and up.",
+ "required" : "0",
+ "order" : 1},
+ "port" : {
+ "getopt" : "n:",
+ "longopt" : "plug",
+ "help" : "-n, --plug=[id] "
+ "Physical plug number on device, UUID or identification of machine",
+ "required" : "1",
+ "order" : 1},
+ "switch" : {
+ "getopt" : "s:",
+ "longopt" : "switch",
+ "help" : "-s, --switch=[id] Physical switch number on device",
+ "required" : "0",
+ "order" : 1},
+ "exec" : {
+ "getopt" : "e:",
+ "longopt" : "exec",
+ "help" : "-e, --exec=[command] Command to execute",
+ "required" : "0",
+ "order" : 1},
+ "vmware_type" : {
+ "getopt" : "d:",
+ "longopt" : "vmware_type",
+ "help" : "-d, --vmware_type=[type] Type of VMware to connect",
+ "required" : "0",
+ "order" : 1},
+ "vmware_datacenter" : {
+ "getopt" : "s:",
+ "longopt" : "vmware-datacenter",
+ "help" : "-s, --vmware-datacenter=[dc] VMWare datacenter filter",
+ "required" : "0",
+ "order" : 2},
+ "snmp_version" : {
+ "getopt" : "d:",
+ "longopt" : "snmp-version",
+ "help" : "-d, --snmp-version=[version] Specifies SNMP version to use (1|2c|3)",
+ "required" : "0",
+ "shortdesc" : "Specifies SNMP version to use",
+ "choices" : ["1", "2c", "3"],
+ "order" : 1},
+ "community" : {
+ "getopt" : "c:",
+ "longopt" : "community",
+ "help" : "-c, --community=[community] Set the community string",
+ "required" : "0",
+ "order" : 1},
+ "snmp_auth_prot" : {
+ "getopt" : "b:",
+ "longopt" : "snmp-auth-prot",
+ "help" : "-b, --snmp-auth-prot=[prot] Set authentication protocol (MD5|SHA)",
+ "required" : "0",
+ "shortdesc" : "Set authentication protocol",
+ "choices" : ["MD5", "SHA"],
+ "order" : 1},
+ "snmp_sec_level" : {
+ "getopt" : "E:",
+ "longopt" : "snmp-sec-level",
+ "help" : "-E, --snmp-sec-level=[level] "
+ "Set security level (noAuthNoPriv|authNoPriv|authPriv)",
+ "required" : "0",
+ "shortdesc" : "Set security level",
+ "choices" : ["noAuthNoPriv", "authNoPriv", "authPriv"],
+ "order" : 1},
+ "snmp_priv_prot" : {
+ "getopt" : "B:",
+ "longopt" : "snmp-priv-prot",
+ "help" : "-B, --snmp-priv-prot=[prot] Set privacy protocol (DES|AES)",
+ "required" : "0",
+ "shortdesc" : "Set privacy protocol",
+ "choices" : ["DES", "AES"],
+ "order" : 1},
+ "snmp_priv_passwd" : {
+ "getopt" : "P:",
+ "longopt" : "snmp-priv-passwd",
+ "help" : "-P, --snmp-priv-passwd=[pass] Set privacy protocol password",
+ "required" : "0",
+ "order" : 1},
+ "snmp_priv_passwd_script" : {
+ "getopt" : "R:",
+ "longopt" : "snmp-priv-passwd-script",
+ "help" : "-R, --snmp-priv-passwd-script Script to run to retrieve privacy password",
+ "required" : "0",
+ "order" : 1},
+ "inet4_only" : {
+ "getopt" : "4",
+ "longopt" : "inet4-only",
+ "help" : "-4, --inet4-only Forces agent to use IPv4 addresses only",
+ "required" : "0",
+ "order" : 1},
+ "inet6_only" : {
+ "getopt" : "6",
+ "longopt" : "inet6-only",
+ "help" : "-6, --inet6-only Forces agent to use IPv6 addresses only",
+ "required" : "0",
+ "order" : 1},
+ "separator" : {
+ "getopt" : "C:",
+ "longopt" : "separator",
+ "help" : "-C, --separator=[char] Separator for CSV created by 'list' operation",
+ "default" : ",",
+ "required" : "0",
+ "order" : 100},
+ "login_timeout" : {
+ "getopt" : ":",
+ "longopt" : "login-timeout",
+ "help" : "--login-timeout=[seconds] Wait X seconds for cmd prompt after login",
+ "default" : "5",
+ "required" : "0",
+ "order" : 200},
+ "shell_timeout" : {
+ "getopt" : ":",
+ "longopt" : "shell-timeout",
+ "help" : "--shell-timeout=[seconds] Wait X seconds for cmd prompt after issuing command",
+ "default" : "3",
+ "required" : "0",
+ "order" : 200},
+ "power_timeout" : {
+ "getopt" : ":",
+ "longopt" : "power-timeout",
+ "help" : "--power-timeout=[seconds] Test X seconds for status change after ON/OFF",
+ "default" : "20",
+ "required" : "0",
+ "order" : 200},
+ "power_wait" : {
+ "getopt" : ":",
+ "longopt" : "power-wait",
+ "help" : "--power-wait=[seconds] Wait X seconds after issuing ON/OFF",
+ "default" : "0",
+ "required" : "0",
+ "order" : 200},
+ "missing_as_off" : {
+ "getopt" : "",
+ "longopt" : "missing-as-off",
+ "help" : "--missing-as-off Missing port returns OFF instead of failure",
+ "required" : "0",
+ "order" : 200},
+ "retry_on" : {
+ "getopt" : ":",
+ "longopt" : "retry-on",
+ "help" : "--retry-on=[attempts] Count of attempts to retry power on",
+ "default" : "1",
+ "required" : "0",
+ "order" : 201},
+ "session_url" : {
+ "getopt" : "s:",
+ "longopt" : "session-url",
+ "help" : "-s, --session-url URL to connect to XenServer on",
+ "required" : "1",
+ "order" : 1},
+ "sudo" : {
+ "getopt" : "",
+ "longopt" : "use-sudo",
+ "help" : "--use-sudo Use sudo (without password) when calling 3rd party software",
+ "required" : "0",
+ "order" : 205},
+ "method" : {
+ "getopt" : "m:",
+ "longopt" : "method",
+ "help" : "-m, --method=[method] Method to fence (onoff|cycle) (Default: onoff)",
+ "required" : "0",
+ "shortdesc" : "Method to fence",
+ "default" : "onoff",
+ "choices" : ["onoff", "cycle"],
+ "order" : 1},
+ "telnet_path" : {
+ "getopt" : ":",
+ "longopt" : "telnet-path",
+ "help" : "--telnet-path=[path] Path to telnet binary",
+ "required" : "0",
+ "default" : "/usr/bin/telnet",
+ "order": 300},
+ "ssh_path" : {
+ "getopt" : ":",
+ "longopt" : "ssh-path",
+ "help" : "--ssh-path=[path] Path to ssh binary",
+ "required" : "0",
+ "default" : "/usr/bin/ssh",
+ "order": 300},
+ "gnutlscli_path" : {
+ "getopt" : ":",
+ "longopt" : "gnutlscli-path",
+ "help" : "--gnutlscli-path=[path] Path to gnutls-cli binary",
+ "required" : "0",
+ "default" : "/usr/bin/gnutls-cli",
+ "order": 300},
+ "sudo_path" : {
+ "getopt" : ":",
+ "longopt" : "sudo-path",
+ "help" : "--sudo-path=[path] Path to sudo binary",
+ "required" : "0",
+ "default" : "/usr/bin/sudo",
+ "order": 300},
+ "snmpwalk_path" : {
+ "getopt" : ":",
+ "longopt" : "snmpwalk-path",
+ "help" : "--snmpwalk-path=[path] Path to snmpwalk binary",
+ "required" : "0",
+ "default" : "/usr/bin/snmpwalk",
+ "order" : 300},
+ "snmpset_path" : {
+ "getopt" : ":",
+ "longopt" : "snmpset-path",
+ "help" : "--snmpset-path=[path] Path to snmpset binary",
+ "required" : "0",
+ "default" : "/usr/bin/snmpset",
+ "order" : 300},
+ "snmpget_path" : {
+ "getopt" : ":",
+ "longopt" : "snmpget-path",
+ "help" : "--snmpget-path=[path] Path to snmpget binary",
+ "required" : "0",
+ "default" : "/usr/bin/snmpget",
+ "order" : 300},
+ "snmp": {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "port_as_ip": {
+ "getopt" : "",
+ "longopt" : "port-as-ip",
+ "help" : "--port-as-ip Make \"port/plug\" to be an alias to IP address",
+ "required" : "0",
+ "order" : 200},
+ "on_target": {
+ "getopt" : "",
+ "help" : "",
+ "order" : 1},
+ "quiet": {
+ "getopt" : "q",
+ "longopt": "quiet",
+ "help" : "-q, --quiet Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog.",
+ "required" : "0",
+ "order" : 50}
+}
+
+# options which are added automatically if 'key' is encountered ("default" is always added)
+DEPENDENCY_OPT = {
+ "default" : ["help", "debug", "verbose", "version", "action", "agent", \
+ "power_timeout", "shell_timeout", "login_timeout", "power_wait", "retry_on", \
+ "delay", "quiet"],
+ "passwd" : ["passwd_script"],
+ "sudo" : ["sudo_path"],
+ "secure" : ["identity_file", "ssh_options", "ssh_path"],
+ "telnet" : ["telnet_path"],
+ "ipaddr" : ["ipport", "inet4_only", "inet6_only"],
+ "port" : ["separator"],
+ "ssl" : ["ssl_secure", "ssl_insecure", "gnutlscli_path"],
+ "snmp" : ["snmp_auth_prot", "snmp_sec_level", "snmp_priv_prot", \
+ "snmp_priv_passwd", "snmp_priv_passwd_script", "community", \
+ "snmpset_path", "snmpget_path", "snmpwalk_path"]
+ }
+
+class fspawn(pexpect.spawn):
+ def __init__(self, options, command):
+ logging.info("Running command: %s", command)
+ pexpect.spawn.__init__(self, command)
+ self.opt = options
+
+ def log_expect(self, pattern, timeout):
+ result = self.expect(pattern, timeout)
+ logging.debug("Received: %s", str(self.before) + str(self.after))
+ return result
+
+ def send(self, message):
+ logging.debug("Sent: %s", message)
+ return pexpect.spawn.send(self, message)
+
+ # send EOL according to what was detected in login process (telnet)
+ def send_eol(self, message):
+ return self.send(message + self.opt["eol"])
+
+def atexit_handler():
+ try:
+ sys.stdout.close()
+ os.close(1)
+ except IOError:
+ logging.error("%s failed to close standard output\n", sys.argv[0])
+ sys.exit(EC_GENERIC_ERROR)
+
+def _add_dependency_options(options):
+ ## Add also options which are available for every fence agent
+ added_opt = []
+ for opt in options + ["default"]:
+ if DEPENDENCY_OPT.has_key(opt):
+ added_opt.extend([y for y in DEPENDENCY_OPT[opt] if options.count(y) == 0])
+
+ if not "port" in (options + added_opt) and \
+ not "nodename" in (options + added_opt) and \
+ "ipaddr" in (options + added_opt):
+ added_opt.append("port_as_ip")
+ all_opt["port"]["help"] = "-n, --plug=[ip] IP address or hostname of fencing device " \
+ "(together with --port-as-ip)"
+
+ return added_opt
+
+def fail_usage(message="", stop=True):
+ if len(message) > 0:
+ logging.error("%s\n", message)
+ if stop:
+ logging.error("Please use '-h' for usage\n")
+ sys.exit(EC_GENERIC_ERROR)
+
+def fail(error_code):
+ message = {
+ EC_LOGIN_DENIED : "Unable to connect/login to fencing device",
+ EC_CONNECTION_LOST : "Connection lost",
+ EC_TIMED_OUT : "Connection timed out",
+ EC_WAITING_ON : "Failed: Timed out waiting to power ON",
+ EC_WAITING_OFF : "Failed: Timed out waiting to power OFF",
+ EC_STATUS : "Failed: Unable to obtain correct plug status or plug is not available",
+ EC_STATUS_HMC : "Failed: Either unable to obtain correct plug status, "
+ "partition is not available or incorrect HMC version used",
+ EC_PASSWORD_MISSING : "Failed: You have to set login password",
+ EC_INVALID_PRIVILEGES : "Failed: The user does not have the correct privileges to do the requested action."
+ }[error_code] + "\n"
+ logging.error("%s\n", message)
+ sys.exit(EC_GENERIC_ERROR)
+
+def usage(avail_opt):
+ print "Usage:"
+ print "\t" + os.path.basename(sys.argv[0]) + " [options]"
+ print "Options:"
+
+ sorted_list = [(key, all_opt[key]) for key in avail_opt]
+ sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"]))
+
+ for key, value in sorted_list:
+ if len(value["help"]) != 0:
+ print " " + _join_wrap([value["help"]], first_indent=3)
+
+def metadata(avail_opt, docs):
+ # avail_opt has to be unique, if there are duplicities then they should be removed
+ sorted_list = [(key, all_opt[key]) for key in list(set(avail_opt))]
+ sorted_list.sort(lambda x, y: cmp(x[0], y[0]))
+ sorted_list.sort(lambda x, y: cmp(x[1]["order"], y[1]["order"]))
+
+ print "<?xml version=\"1.0\" ?>"
+ print "<resource-agent name=\"" + os.path.basename(sys.argv[0]) + \
+ "\" shortdesc=\"" + docs["shortdesc"] + "\" >"
+ for (symlink, desc) in docs.get("symlink", []):
+ print "<symlink name=\"" + symlink + "\" shortdesc=\"" + desc + "\"/>"
+ print "<longdesc>" + docs["longdesc"] + "</longdesc>"
+ print "<vendor-url>" + docs["vendorurl"] + "</vendor-url>"
+ print "<parameters>"
+ for option, _ in sorted_list:
+ if all_opt[option].has_key("help") and len(all_opt[option]["help"]) > 0:
+ print "\t<parameter name=\"" + option + "\" unique=\"0\" required=\"" + all_opt[option]["required"] + "\">"
+
+ default = ""
+ if all_opt[option].has_key("default"):
+ default = "default=\"" + _encode_html_entities(str(all_opt[option]["default"])) + "\" "
+
+ mixed = all_opt[option]["help"]
+ ## split it between option and help text
+ res = re.compile(r"^(.*?--\S+)\s+", re.IGNORECASE | re.S).search(mixed)
+ if None != res:
+ mixed = res.group(1)
+ mixed = _encode_html_entities(mixed)
+
+ if not "shortdesc" in all_opt[option]:
+ shortdesc = re.sub("\s\s+", " ", all_opt[option]["help"][31:])
+ else:
+ shortdesc = all_opt[option]["shortdesc"]
+
+ print "\t\t<getopt mixed=\"" + mixed + "\" />"
+ if all_opt[option].has_key("choices"):
+ print "\t\t<content type=\"select\" "+default+" >"
+ for choice in all_opt[option]["choices"]:
+ print "\t\t\t<option value=\"%s\" />" % (choice)
+ print "\t\t</content>"
+ elif all_opt[option]["getopt"].count(":") > 0:
+ print "\t\t<content type=\"string\" "+default+" />"
+ else:
+ print "\t\t<content type=\"boolean\" "+default+" />"
+ print "\t\t<shortdesc lang=\"en\">" + shortdesc + "</shortdesc>"
+ print "\t</parameter>"
+ print "</parameters>"
+ print "<actions>"
+
+ (available_actions, _) = _get_available_actions(avail_opt)
+
+ if "on" in available_actions:
+ available_actions.remove("on")
+ on_target = ' on_target="1"' if avail_opt.count("on_target") else ''
+ print "\t<action name=\"on\"%s automatic=\"%d\"/>" % (on_target, avail_opt.count("fabric_fencing"))
+
+ for action in available_actions:
+ print "\t<action name=\"%s\" />" % (action)
+ print "</actions>"
+ print "</resource-agent>"
+
+def process_input(avail_opt):
+ avail_opt.extend(_add_dependency_options(avail_opt))
+
+ # @todo: this should be put elsewhere?
+ os.putenv("LANG", "C")
+ os.putenv("LC_ALL", "C")
+
+ if "port_as_ip" in avail_opt:
+ avail_opt.append("port")
+
+ if len(sys.argv) > 1:
+ opt = _parse_input_cmdline(avail_opt)
+ else:
+ opt = _parse_input_stdin(avail_opt)
+
+ if "--port-as-ip" in opt and "--plug" in opt:
+ opt["--ip"] = opt["--plug"]
+
+ return opt
+
+##
+## This function checks input and answers if we want to have same answers
+## in each of the fencing agents. It looks for possible errors and run
+## password script to set a correct password
+######
+def check_input(device_opt, opt, other_conditions = False):
+ device_opt.extend(_add_dependency_options(device_opt))
+
+ options = dict(opt)
+ options["device_opt"] = device_opt
+
+ _update_metadata(options)
+ options = _set_default_values(options)
+ options["--action"] = options["--action"].lower()
+
+ ## In special cases (show help, metadata or version) we don't need to check anything
+ #####
+ # OCF compatibility
+ if options["--action"] == "meta-data":
+ options["--action"] = "metadata"
+
+ if options["--action"] == "metadata" or any(options.has_key(k) for k in ("--help", "--version")):
+ return options
+
+ if options.has_key("--verbose"):
+ logging.getLogger().setLevel(logging.DEBUG)
+
+ ## add logging to syslog
+ logging.getLogger().addHandler(SyslogLibHandler())
+ if not options.has_key("--quiet"):
+ ## add logging to stderr
+ logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
+
+ (acceptable_actions, _) = _get_available_actions(device_opt)
+
+ if 1 == device_opt.count("fabric_fencing"):
+ acceptable_actions.extend(["enable", "disable"])
+
+ if 0 == acceptable_actions.count(options["--action"]):
+ fail_usage("Failed: Unrecognised action '" + options["--action"] + "'")
+
+ ## Compatibility layer
+ #####
+ if options["--action"] == "enable":
+ options["--action"] = "on"
+ if options["--action"] == "disable":
+ options["--action"] = "off"
+
+
+ if options["--action"] == "validate-all" and not other_conditions:
+ _validate_input(options, False)
+ sys.exit(EC_OK)
+ else:
+ _validate_input(options, True)
+
+ if options.has_key("--debug-file"):
+ try:
+ debug_file = logging.FileHandler(options["--debug-file"])
+ debug_file.setLevel(logging.DEBUG)
+ logging.getLogger().addHandler(debug_file)
+ except IOError:
+ logging.error("Unable to create file %s", options["--debug-file"])
+ fail_usage("Failed: Unable to create file " + options["--debug-file"])
+
+ if options.has_key("--snmp-priv-passwd-script"):
+ options["--snmp-priv-passwd"] = os.popen(options["--snmp-priv-passwd-script"]).read().rstrip()
+
+ if options.has_key("--password-script"):
+ options["--password"] = os.popen(options["--password-script"]).read().rstrip()
+
+ return options
+
+## Obtain a power status from possibly more than one plug
+## "on" is returned if at least one plug is ON
+######
+def get_multi_power_fn(connection, options, get_power_fn):
+ status = "off"
+ plugs = options["--plugs"] if options.has_key("--plugs") else [""]
+
+ for plug in plugs:
+ try:
+ options["--uuid"] = str(uuid.UUID(plug))
+ except ValueError:
+ pass
+ except KeyError:
+ pass
+
+ options["--plug"] = plug
+ plug_status = get_power_fn(connection, options)
+ if plug_status != "off":
+ status = plug_status
+
+ return status
+
+def set_multi_power_fn(connection, options, set_power_fn, get_power_fn, retry_attempts=1):
+ plugs = options["--plugs"] if options.has_key("--plugs") else [""]
+
+ for _ in range(retry_attempts):
+ for plug in plugs:
+ try:
+ options["--uuid"] = str(uuid.UUID(plug))
+ except ValueError:
+ pass
+ except KeyError:
+ pass
+
+ options["--plug"] = plug
+ set_power_fn(connection, options)
+ time.sleep(int(options["--power-wait"]))
+
+ for _ in xrange(int(options["--power-timeout"])):
+ if get_multi_power_fn(connection, options, get_power_fn) != options["--action"]:
+ time.sleep(1)
+ else:
+ return True
+ return False
+
+def show_docs(options, docs=None):
+ device_opt = options["device_opt"]
+
+ if docs == None:
+ docs = {}
+ docs["shortdesc"] = "Fence agent"
+ docs["longdesc"] = ""
+
+ if options.has_key("--help"):
+ usage(device_opt)
+ sys.exit(0)
+
+ if options.get("--action", "") == "metadata":
+ if "port_as_ip" in device_opt:
+ device_opt.remove("separator")
+ metadata(device_opt, docs)
+ sys.exit(0)
+
+ if options.has_key("--version"):
+ print __main__.RELEASE_VERSION, __main__.BUILD_DATE
+ print __main__.REDHAT_COPYRIGHT
+ sys.exit(0)
+
+def fence_action(connection, options, set_power_fn, get_power_fn, get_outlet_list=None, reboot_cycle_fn=None):
+ result = 0
+
+ try:
+ if options.has_key("--plug"):
+ options["--plugs"] = options["--plug"].split(",")
+
+ ## Process options that manipulate fencing device
+ #####
+ if (options["--action"] in ["list", "list-status"]) or \
+ ((options["--action"] == "monitor") and 1 == options["device_opt"].count("port") and \
+ 0 == options["device_opt"].count("port_as_ip")):
+
+ if 0 == options["device_opt"].count("port"):
+ print "N/A"
+ elif get_outlet_list == None:
+ ## @todo: exception?
+ ## This is just temporal solution, we will remove default value
+ ## None as soon as all existing agent will support this operation
+ print "NOTICE: List option is not working on this device yet"
+ else:
+ options["--original-action"] = options["--action"]
+ options["--action"] = "list"
+ outlets = get_outlet_list(connection, options)
+ options["--action"] = options["--original-action"]
+ del options["--original-action"]
+
+ ## keys can be numbers (port numbers) or strings (names of VM, UUID)
+ for outlet_id in outlets.keys():
+ (alias, status) = outlets[outlet_id]
+ if status is None or (not status.upper() in ["ON", "OFF"]):
+ status = "UNKNOWN"
+ status = status.upper()
+
+ if options["--action"] == "list":
+ print outlet_id + options["--separator"] + alias
+ elif options["--action"] == "list-status":
+ print outlet_id + options["--separator"] + alias + options["--separator"] + status
+
+ return
+
+ if options["--action"] == "monitor" and not "port" in options["device_opt"] and "no_status" in options["device_opt"]:
+ # Unable to do standard monitoring because 'status' action is not available
+ return 0
+
+ status = None
+ if not "no_status" in options["device_opt"]:
+ status = get_multi_power_fn(connection, options, get_power_fn)
+ if status != "on" and status != "off":
+ fail(EC_STATUS)
+
+ if options["--action"] == status:
+ if not (status == "on" and "force_on" in options["device_opt"]):
+ print "Success: Already %s" % (status.upper())
+ return 0
+
+ if options["--action"] == "on":
+ if set_multi_power_fn(connection, options, set_power_fn, get_power_fn, 1 + int(options["--retry-on"])):
+ print "Success: Powered ON"
+ else:
+ fail(EC_WAITING_ON)
+ elif options["--action"] == "off":
+ if set_multi_power_fn(connection, options, set_power_fn, get_power_fn):
+ print "Success: Powered OFF"
+ else:
+ fail(EC_WAITING_OFF)
+ elif options["--action"] == "reboot":
+ power_on = False
+ if options.get("--method", "").lower() == "cycle" and reboot_cycle_fn is not None:
+ for _ in range(1, 1 + int(options["--retry-on"])):
+ if reboot_cycle_fn(connection, options):
+ power_on = True
+ break
+
+ if not power_on:
+ fail(EC_TIMED_OUT)
+
+ else:
+ if status != "off":
+ options["--action"] = "off"
+ if not set_multi_power_fn(connection, options, set_power_fn, get_power_fn):
+ fail(EC_WAITING_OFF)
+
+ options["--action"] = "on"
+
+ try:
+ power_on = set_multi_power_fn(connection, options, set_power_fn, get_power_fn, int(options["--retry-on"]))
+ except Exception, ex:
+ # an error occured during power ON phase in reboot
+ # fence action was completed succesfully even in that case
+ logging.warning("%s", str(ex))
+
+ if power_on == False:
+ # this should not fail as node was fenced succesfully
+ logging.error('Timed out waiting to power ON\n')
+
+ print "Success: Rebooted"
+ elif options["--action"] == "status":
+ print "Status: " + status.upper()
+ if status.upper() == "OFF":
+ result = 2
+ elif options["--action"] == "monitor":
+ pass
+ except pexpect.EOF:
+ fail(EC_CONNECTION_LOST)
+ except pexpect.TIMEOUT:
+ fail(EC_TIMED_OUT)
+ 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
+
+def fence_login(options, re_login_string=r"(login\s*: )|((?!Last )Login Name: )|(username: )|(User Name :)"):
+ run_delay(options)
+
+ if not options.has_key("eol"):
+ options["eol"] = "\r\n"
+
+ if options.has_key("--command-prompt") and type(options["--command-prompt"]) is not list:
+ options["--command-prompt"] = [options["--command-prompt"]]
+
+ try:
+ if options.has_key("--ssl"):
+ conn = _open_ssl_connection(options)
+ elif options.has_key("--ssh") and not options.has_key("--identity-file"):
+ conn = _login_ssh_with_password(options, re_login_string)
+ elif options.has_key("--ssh") and options.has_key("--identity-file"):
+ conn = _login_ssh_with_identity_file(options)
+ else:
+ conn = _login_telnet(options, re_login_string)
+ except pexpect.EOF, exception:
+ logging.debug("%s", str(exception))
+ fail(EC_LOGIN_DENIED)
+ except pexpect.TIMEOUT, exception:
+ logging.debug("%s", str(exception))
+ fail(EC_LOGIN_DENIED)
+ return conn
+
+def is_executable(path):
+ if os.path.exists(path):
+ stats = os.stat(path)
+ if stat.S_ISREG(stats.st_mode) and os.access(path, os.X_OK):
+ return True
+ return False
+
+def run_command(options, command, timeout=None, env=None, log_command=None):
+ if timeout is None and "--power-timeout" in options:
+ timeout = options["--power-timeout"]
+ if timeout is not None:
+ timeout = float(timeout)
+
+ logging.info("Executing: %s\n", log_command or command)
+
+ try:
+ process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+ except OSError:
+ fail_usage("Unable to run %s\n" % command)
+
+ thread = threading.Thread(target=process.wait)
+ thread.start()
+ thread.join(timeout)
+ if thread.is_alive():
+ process.kill()
+ fail(EC_TIMED_OUT)
+
+ status = process.wait()
+
+ (pipe_stdout, pipe_stderr) = process.communicate()
+ process.stdout.close()
+ process.stderr.close()
+
+ logging.debug("%s %s %s\n", str(status), str(pipe_stdout), str(pipe_stderr))
+
+ return (status, pipe_stdout, pipe_stderr)
+
+def run_delay(options):
+ ## Delay is important for two-node clusters fencing but we do not need to delay 'status' operations
+ if options["--action"] in ["off", "reboot"]:
+ logging.info("Delay %s second(s) before logging in to the fence device", options["--delay"])
+ time.sleep(int(options["--delay"]))
+
+def fence_logout(conn, logout_string, sleep=0):
+ # Logout is not required part of fencing but we should attempt to do it properly
+ # In some cases our 'exit' command is faster and we can not close connection as it
+ # was already closed by fencing device
+ try:
+ conn.send_eol(logout_string)
+ time.sleep(sleep)
+ conn.close()
+ except exceptions.OSError:
+ pass
+ except pexpect.ExceptionPexpect:
+ pass
+
+# Convert array of format [[key1, value1], [key2, value2], ... [keyN, valueN]] to dict, where key is
+# in format a.b.c.d...z and returned dict has key only z
+def array_to_dict(array):
+ return dict([[x[0].split(".")[-1], x[1]] for x in array])
+
+## Own logger handler that uses old-style syslog handler as otherwise everything is sourced
+## from /dev/syslog
+class SyslogLibHandler(logging.StreamHandler):
+ """
+ A handler class that correctly push messages into syslog
+ """
+ def emit(self, record):
+ syslog_level = {
+ logging.CRITICAL:syslog.LOG_CRIT,
+ logging.ERROR:syslog.LOG_ERR,
+ logging.WARNING:syslog.LOG_WARNING,
+ logging.INFO:syslog.LOG_INFO,
+ logging.DEBUG:syslog.LOG_DEBUG,
+ logging.NOTSET:syslog.LOG_DEBUG,
+ }[record.levelno]
+
+ msg = self.format(record)
+
+ # syslos.syslog can not have 0x00 character inside or exception is thrown
+ syslog.syslog(syslog_level, msg.replace("\x00", "\n"))
+ return
+
+def _open_ssl_connection(options):
+ gnutls_opts = ""
+ ssl_opts = ""
+
+ if options.has_key("--notls"):
+ gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:-VERS-TLS1.0:+VERS-SSL3.0\""
+ elif options.has_key("--tls1.0"):
+ gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:+VERS-TLS1.0:%LATEST_RECORD_VERSION\""
+
+ # --ssl is same as the --ssl-secure; it means we want to verify certificate in these cases
+ if options.has_key("--ssl-insecure"):
+ ssl_opts = "--insecure"
+
+ command = '%s %s %s --crlf -p %s %s' % \
+ (options["--gnutlscli-path"], gnutls_opts, ssl_opts, options["--ipport"], options["--ip"])
+ try:
+ conn = fspawn(options, command)
+ except pexpect.ExceptionPexpect, ex:
+ logging.error("%s\n", str(ex))
+ sys.exit(EC_GENERIC_ERROR)
+
+ return conn
+
+def _login_ssh_with_identity_file(options):
+ if options.has_key("--inet6-only"):
+ force_ipvx = "-6 "
+ elif options.has_key("--inet4-only"):
+ force_ipvx = "-4 "
+ else:
+ force_ipvx = ""
+
+ command = '%s %s %s@%s -i %s -p %s' % \
+ (options["--ssh-path"], force_ipvx, options["--username"], options["--ip"], \
+ options["--identity-file"], options["--ipport"])
+ if options.has_key("--ssh-options"):
+ command += ' ' + options["--ssh-options"]
+
+ conn = fspawn(options, command)
+
+ result = conn.log_expect(["Enter passphrase for key '" + options["--identity-file"] + "':", \
+ "Are you sure you want to continue connecting (yes/no)?"] + \
+ options["--command-prompt"], int(options["--login-timeout"]))
+ if result == 1:
+ conn.sendline("yes")
+ result = conn.log_expect(
+ ["Enter passphrase for key '" + options["--identity-file"]+"':"] + \
+ options["--command-prompt"], int(options["--login-timeout"]))
+ if result == 0:
+ if options.has_key("--password"):
+ conn.sendline(options["--password"])
+ conn.log_expect(options["--command-prompt"], int(options["--login-timeout"]))
+ else:
+ fail_usage("Failed: You have to enter passphrase (-p) for identity file")
+
+ return conn
+
+def _login_telnet(options, re_login_string):
+ re_login = re.compile(re_login_string, re.IGNORECASE)
+ re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE)
+
+ conn = fspawn(options, options["--telnet-path"])
+ conn.send("set binary\n")
+ conn.send("open %s -%s\n"%(options["--ip"], options["--ipport"]))
+
+ conn.log_expect(re_login, int(options["--login-timeout"]))
+ conn.send_eol(options["--username"])
+
+ ## automatically change end of line separator
+ screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"]))
+ if re_login.search(screen) != None:
+ options["eol"] = "\n"
+ conn.send_eol(options["--username"])
+ conn.log_expect(re_pass, int(options["--login-timeout"]))
+ elif re_pass.search(screen) == None:
+ conn.log_expect(re_pass, int(options["--shell-timeout"]))
+
+ try:
+ conn.send_eol(options["--password"])
+ valid_password = conn.log_expect([re_login] + \
+ options["--command-prompt"], int(options["--shell-timeout"]))
+ if valid_password == 0:
+ ## password is invalid or we have to change EOL separator
+ options["eol"] = "\r"
+ conn.send_eol("")
+ screen = conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"]))
+ ## after sending EOL the fence device can either show 'Login' or 'Password'
+ if re_login.search(conn.after + screen) != None:
+ conn.send_eol("")
+ conn.send_eol(options["--username"])
+ conn.log_expect(re_pass, int(options["--login-timeout"]))
+ conn.send_eol(options["--password"])
+ conn.log_expect(options["--command-prompt"], int(options["--login-timeout"]))
+ except KeyError:
+ fail(EC_PASSWORD_MISSING)
+
+ return conn
+
+def _login_ssh_with_password(options, re_login_string):
+ re_login = re.compile(re_login_string, re.IGNORECASE)
+ re_pass = re.compile("(password)|(pass phrase)", re.IGNORECASE)
+
+ if options.has_key("--inet6-only"):
+ force_ipvx = "-6 "
+ elif options.has_key("--inet4-only"):
+ force_ipvx = "-4 "
+ else:
+ force_ipvx = ""
+
+ command = '%s %s %s@%s -p %s -o PubkeyAuthentication=no' % \
+ (options["--ssh-path"], force_ipvx, options["--username"], options["--ip"], options["--ipport"])
+ if options.has_key("--ssh-options"):
+ command += ' ' + options["--ssh-options"]
+
+ conn = fspawn(options, command)
+
+ if options.has_key("telnet_over_ssh"):
+ # This is for stupid ssh servers (like ALOM) which behave more like telnet
+ # (ignore name and display login prompt)
+ result = conn.log_expect( \
+ [re_login, "Are you sure you want to continue connecting (yes/no)?"],
+ int(options["--login-timeout"]))
+ if result == 1:
+ conn.sendline("yes") # Host identity confirm
+ conn.log_expect(re_login, int(options["--login-timeout"]))
+
+ conn.sendline(options["--username"])
+ conn.log_expect(re_pass, int(options["--login-timeout"]))
+ else:
+ result = conn.log_expect( \
+ ["ssword:", "Are you sure you want to continue connecting (yes/no)?"],
+ int(options["--login-timeout"]))
+ if result == 1:
+ conn.sendline("yes")
+ conn.log_expect("ssword:", int(options["--login-timeout"]))
+
+ conn.sendline(options["--password"])
+ conn.log_expect(options["--command-prompt"], int(options["--login-timeout"]))
+
+ return conn
+
+#
+# To update metadata, we change values in all_opt
+def _update_metadata(options):
+ device_opt = options["device_opt"]
+
+ if device_opt.count("login") and device_opt.count("no_login") == 0:
+ all_opt["login"]["required"] = "1"
+ else:
+ all_opt["login"]["required"] = "0"
+
+ if device_opt.count("port_as_ip"):
+ all_opt["ipaddr"]["required"] = "0"
+ all_opt["port"]["required"] = "0"
+
+ (available_actions, default_value) = _get_available_actions(device_opt)
+ all_opt["action"]["default"] = default_value
+
+ actions_with_default = \
+ [x if not x == all_opt["action"]["default"] else x + " (default)" for x in available_actions]
+ all_opt["action"]["help"] = \
+ "-o, --action=[action] Action: %s" % (_join_wrap(actions_with_default, last_separator=" or "))
+
+ if device_opt.count("ipport"):
+ default_value = None
+ default_string = None
+
+ if all_opt["ipport"].has_key("default"):
+ default_value = all_opt["ipport"]["default"]
+ elif device_opt.count("web") and device_opt.count("ssl"):
+ default_value = "80"
+ default_string = "(default 80, 443 if --ssl option is used)"
+ elif device_opt.count("telnet") and device_opt.count("secure"):
+ default_value = "23"
+ default_string = "(default 23, 22 if --ssh option is used)"
+ else:
+ tcp_ports = {"community" : "161", "secure" : "22", "telnet" : "23", "web" : "80", "ssl" : "443"}
+ # all cases where next command returns multiple results are covered by previous blocks
+ protocol = [x for x in ["community", "secure", "ssl", "web", "telnet"] if device_opt.count(x)][0]
+ default_value = tcp_ports[protocol]
+
+ if default_string is None:
+ all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use (default %s)" % \
+ (default_value)
+ else:
+ all_opt["ipport"]["help"] = "-u, --ipport=[port] TCP/UDP port to use\n" + " "*40 + default_string
+
+def _set_default_values(options):
+ if "ipport" in options["device_opt"]:
+ if not "--ipport" in options:
+ if "default" in all_opt["ipport"]:
+ options["--ipport"] = all_opt["ipport"]["default"]
+ elif "community" in options["device_opt"]:
+ options["--ipport"] = "161"
+ elif "--ssh" in options or all_opt["secure"].get("default", "0") == "1":
+ options["--ipport"] = "22"
+ elif "--ssl" in options or all_opt["ssl"].get("default", "0") == "1":
+ options["--ipport"] = "443"
+ elif "--ssl-secure" in options or all_opt["ssl_secure"].get("default", "0") == "1":
+ options["--ipport"] = "443"
+ elif "--ssl-insecure" in options or all_opt["ssl_insecure"].get("default", "0") == "1":
+ options["--ipport"] = "443"
+ elif "web" in options["device_opt"]:
+ options["--ipport"] = "80"
+ elif "telnet" in options["device_opt"]:
+ options["--ipport"] = "23"
+
+ if "--ipport" in options:
+ all_opt["ipport"]["default"] = options["--ipport"]
+
+ for opt in options["device_opt"]:
+ if all_opt[opt].has_key("default") and not opt == "ipport":
+ getopt_long = "--" + all_opt[opt]["longopt"]
+ if not options.has_key(getopt_long):
+ options[getopt_long] = all_opt[opt]["default"]
+
+ return options
+
+# stop = True/False : exit fence agent when problem is encountered
+def _validate_input(options, stop = True):
+ device_opt = options["device_opt"]
+ valid_input = True
+
+ if not options.has_key("--username") and \
+ device_opt.count("login") and (device_opt.count("no_login") == 0):
+ valid_input = False
+ fail_usage("Failed: You have to set login name", stop)
+
+ if device_opt.count("ipaddr") and not options.has_key("--ip") and not options.has_key("--managed"):
+ valid_input = False
+ fail_usage("Failed: You have to enter fence address", stop)
+
+ if device_opt.count("no_password") == 0:
+ if 0 == device_opt.count("identity_file"):
+ if not (options.has_key("--password") or options.has_key("--password-script")):
+ valid_input = False
+ fail_usage("Failed: You have to enter password or password script", stop)
+ else:
+ if not (options.has_key("--password") or \
+ options.has_key("--password-script") or options.has_key("--identity-file")):
+ valid_input = False
+ fail_usage("Failed: You have to enter password, password script or identity file", stop)
+
+ if not options.has_key("--ssh") and options.has_key("--identity-file"):
+ valid_input = False
+ fail_usage("Failed: You have to use identity file together with ssh connection (-x)", stop)
+
+ if options.has_key("--identity-file") and not os.path.isfile(options["--identity-file"]):
+ valid_input = False
+ fail_usage("Failed: Identity file " + options["--identity-file"] + " does not exist", stop)
+
+ if (0 == ["list", "list-status", "monitor"].count(options["--action"])) and \
+ not options.has_key("--plug") and device_opt.count("port") and \
+ device_opt.count("no_port") == 0 and not device_opt.count("port_as_ip"):
+ valid_input = False
+ fail_usage("Failed: You have to enter plug number or machine identification", stop)
+
+ if options.has_key("--plug") and len(options["--plug"].split(",")) > 1 and \
+ options.has_key("--method") and options["--method"] == "cycle":
+ valid_input = False
+ fail_usage("Failed: Cannot use --method cycle for more than 1 plug", stop)
+
+ for failed_opt in _get_opts_with_invalid_choices(options):
+ valid_input = False
+ fail_usage("Failed: You have to enter a valid choice for %s from the valid values: %s" % \
+ ("--" + all_opt[failed_opt]["longopt"], str(all_opt[failed_opt]["choices"])), stop)
+
+ return valid_input
+
+def _encode_html_entities(text):
+ return text.replace("&", "&amp;").replace('"', "&quot;").replace('<', "&lt;"). \
+ replace('>', "&gt;").replace("'", "&apos;")
+
+def _prepare_getopt_args(options):
+ getopt_string = ""
+ longopt_list = []
+ for k in options:
+ if all_opt.has_key(k) and all_opt[k]["getopt"] != ":":
+ # getopt == ":" means that opt is without short getopt, but has value
+ getopt_string += all_opt[k]["getopt"]
+ elif not all_opt.has_key(k):
+ fail_usage("Parse error: unknown option '"+k+"'")
+
+ if all_opt.has_key(k) and all_opt[k].has_key("longopt"):
+ if all_opt[k]["getopt"].endswith(":"):
+ longopt_list.append(all_opt[k]["longopt"] + "=")
+ else:
+ longopt_list.append(all_opt[k]["longopt"])
+
+ return (getopt_string, longopt_list)
+
+def _parse_input_stdin(avail_opt):
+ opt = {}
+ name = ""
+ for line in sys.stdin.readlines():
+ line = line.strip()
+ if (line.startswith("#")) or (len(line) == 0):
+ continue
+
+ (name, value) = (line + "=").split("=", 1)
+ value = value[:-1]
+
+ if avail_opt.count(name) == 0 and name in ["nodename"]:
+ continue
+ elif avail_opt.count(name) == 0:
+ logging.warning("Parse error: Ignoring unknown option '%s'\n", line)
+ continue
+
+ if all_opt[name]["getopt"].endswith(":"):
+ opt["--"+all_opt[name]["longopt"].rstrip(":")] = value
+ elif value.lower() in ["1", "yes", "on", "true"]:
+ opt["--"+all_opt[name]["longopt"]] = "1"
+ else:
+ logging.warning("Parse error: Ignoring option '%s' because it does not have value\n", name)
+ return opt
+
+def _parse_input_cmdline(avail_opt):
+ filtered_opts = {}
+ _verify_unique_getopt(avail_opt)
+ (getopt_string, longopt_list) = _prepare_getopt_args(avail_opt)
+
+ try:
+ (entered_opt, left_arg) = getopt.gnu_getopt(sys.argv[1:], getopt_string, longopt_list)
+ if len(left_arg) > 0:
+ logging.warning("Unused arguments on command line: %s" % (str(left_arg)))
+ except getopt.GetoptError, error:
+ fail_usage("Parse error: " + error.msg)
+
+ for opt in avail_opt:
+ filtered_opts.update({opt : all_opt[opt]})
+
+ # Short and long getopt names are changed to consistent "--" + long name (e.g. --username)
+ long_opts = {}
+ for arg_name in dict(entered_opt).keys():
+ all_key = [key for (key, value) in filtered_opts.items() \
+ if "--" + value.get("longopt", "") == arg_name or "-" + value.get("getopt", "").rstrip(":") == arg_name][0]
+ long_opts["--" + filtered_opts[all_key]["longopt"]] = dict(entered_opt)[arg_name]
+
+ # This test is specific because it does not apply to input on stdin
+ if "port_as_ip" in avail_opt and not "--port-as-ip" in long_opts and "--plug" in long_opts:
+ fail_usage("Parser error: option -n/--plug is not recognized")
+
+ return long_opts
+
+# for ["John", "Mary", "Eli"] returns "John, Mary and Eli"
+def _join2(words, normal_separator=", ", last_separator=" and "):
+ if len(words) <= 1:
+ return "".join(words)
+ else:
+ return last_separator.join([normal_separator.join(words[:-1]), words[-1]])
+
+def _join_wrap(words, normal_separator=", ", last_separator=" and ", first_indent=42):
+ x = _join2(words, normal_separator, last_separator)
+ wrapper = textwrap.TextWrapper()
+ wrapper.initial_indent = " "*first_indent
+ wrapper.subsequent_indent = " "*40
+ wrapper.width = 85
+ wrapper.break_on_hyphens = False
+ wrapper.break_long_words = False
+ wrapped_text = ""
+ for line in wrapper.wrap(x):
+ wrapped_text += line + "\n"
+ return wrapped_text.lstrip().rstrip("\n")
+
+def _get_opts_with_invalid_choices(options):
+ options_failed = []
+ device_opt = options["device_opt"]
+
+ for opt in device_opt:
+ if all_opt[opt].has_key("choices"):
+ longopt = "--" + all_opt[opt]["longopt"]
+ possible_values_upper = [y.upper() for y in all_opt[opt]["choices"]]
+ if options.has_key(longopt):
+ options[longopt] = options[longopt].upper()
+ if not options["--" + all_opt[opt]["longopt"]] in possible_values_upper:
+ options_failed.append(opt)
+ return options_failed
+
+def _verify_unique_getopt(avail_opt):
+ used_getopt = set()
+
+ for opt in avail_opt:
+ getopt_value = all_opt[opt].get("getopt", "").rstrip(":")
+ if getopt_value and getopt_value in used_getopt:
+ fail_usage("Short getopt for %s (-%s) is not unique" % (opt, getopt_value))
+ else:
+ used_getopt.add(getopt_value)
+
+def _get_available_actions(device_opt):
+ available_actions = ["on", "off", "reboot", "status", "list", "list-status", \
+ "monitor", "metadata", "validate-all"]
+ default_value = "reboot"
+
+ if device_opt.count("fabric_fencing"):
+ available_actions.remove("reboot")
+ default_value = "off"
+ if device_opt.count("no_status"):
+ available_actions.remove("status")
+ if device_opt.count("no_on"):
+ available_actions.remove("on")
+ if device_opt.count("no_off"):
+ available_actions.remove("off")
+ if not device_opt.count("separator"):
+ available_actions.remove("list")
+ available_actions.remove("list-status")
+
+ return (available_actions, default_value)