diff --git a/Makefile.am b/Makefile.am index e5ee3203..4560002d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,49 +1,47 @@ -EXTRA_DIST = autogen.sh make/fencebuild.mk scripts/fenceparse \ +EXTRA_DIST = autogen.sh make/fencebuild.mk \ .version make/release.mk \ make/git-version-gen make/gitlog-to-changelog tests AUTOMAKE_OPTIONS = foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ autoheader automake autoconf libtool libtoolize \ ltmain.sh compile make/clusterautoconfig.h.in \ make/clusterautoconfig.h.in~ autoscan.log \ configure.scan -noinst_HEADERS = make/copyright.cf - ACLOCAL_AMFLAGS = -I m4 SUBDIRS = fence/agents/lib fence doc install-exec-local: $(INSTALL) -d $(DESTDIR)/$(LOGDIR) $(INSTALL) -d $(DESTDIR)/$(CLUSTERVARRUN) uninstall-local: rmdir $(DESTDIR)/$(LOGDIR) || :; rmdir $(DESTDIR)/$(CLUSTERVARRUN) || :; BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: gen-ChangeLog echo $(VERSION) > $(distdir)/.tarball-version gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d .git; then \ $(top_srcdir)/make/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi # this will get rid of "libtoolized" m4 files maintainer-clean-local: rm -rf $(filter-out \ $(top_srcdir)/m4/ac_python_module.m4,$(wildcard \ $(top_srcdir)/m4/*.m4)) diff --git a/fence/agents/alom/fence_alom.py b/fence/agents/alom/fence_alom.py index 62ffd7d0..7b03dc2a 100644 --- a/fence/agents/alom/fence_alom.py +++ b/fence/agents/alom/fence_alom.py @@ -1,59 +1,53 @@ #!@PYTHON@ -tt # The Following Agent Has Been Tested On: # # Sun(tm) Advanced Lights Out Manager CMT v1.6.1 # as found on SUN T2000 Niagara import sys, re, time import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Sun Advanced Lights Out Manager (ALOM)" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(conn, options): conn.send_eol("showplatform") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) status = re.search("standby", conn.before.lower()) result = (status != None and "off" or "on") return result def set_power_status(conn, options): cmd_line = (options["--action"] == "on" and "poweron" or "poweroff -f -y") conn.send_eol(cmd_line) conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) # Get the machine some time between poweron and poweroff time.sleep(int(options["--power-timeout"])) def main(): device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure"] atexit.register(atexit_handler) all_opt["secure"]["default"] = "1" all_opt["cmd_prompt"]["default"] = [r"sc\>\ "] options = check_input(device_opt, process_input(device_opt)) options["telnet_over_ssh"] = 1 docs = {} docs["shortdesc"] = "Fence agent for Sun ALOM" docs["longdesc"] = "fence_alom is an I/O Fencing \ agent which can be used with ALOM connected machines." docs["vendorurl"] = "http://www.sun.com" show_docs(options, docs) # Operate the fencing device conn = fence_login(options) result = fence_action(conn, options, set_power_status, get_power_status, None) fence_logout(conn, "logout") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/amt/fence_amt.py b/fence/agents/amt/fence_amt.py index 082f5d09..feec6e3e 100644 --- a/fence/agents/amt/fence_amt.py +++ b/fence/agents/amt/fence_amt.py @@ -1,134 +1,128 @@ #!@PYTHON@ -tt import sys, re, os import atexit from pipes import quote sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, is_executable, run_command, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Fence agent for Intel AMT" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(_, options): output = amt_run_command(options, create_command(options, "status")) match = re.search('Powerstate:[\\s]*(..)', str(output)) status = match.group(1) if match else None if status == None: return "fail" elif status == "S0": # SO = on; S3 = sleep; S5 = off return "on" else: return "off" def set_power_status(_, options): amt_run_command(options, create_command(options, options["--action"])) return def reboot_cycle(_, options): (status, _, _) = run_command(options, create_command(options, "cycle")) return not bool(status) def amt_run_command(options, command, timeout=None): env = os.environ.copy() x = quote(options["--password"]) x = x[:-1] if x.endswith("'") else x x = x[1:] if x.startswith("'") else x env["AMT_PASSWORD"] = x # This is needed because setting the AMT_PASSWORD env # variable only works when no pipe is involved. E.g.: # - Broken: # $ AMT_PASSWORD='foobar' echo 'y' | /usr/bin/amttool nuc2 powerdown # 401 Unauthorized at /usr/bin/amttool line 129. # - Working: # $ AMT_PASSWORD='foobar' sh -c "(echo 'y' | /usr/bin/amttool nuc2 powerdown)" # execute: powerdown # result: pt_status: success newcommand = "sh -c \"(%s)\"" % command return run_command(options, newcommand, timeout, env) def create_command(options, action): cmd = options["--amttool-path"] # --ip / -a cmd += " " + options["--ip"] # --action / -o if action == "status": cmd += " info" elif action == "on": cmd = "echo \"y\"|" + cmd cmd += " powerup" elif action == "off": cmd = "echo \"y\"|" + cmd cmd += " powerdown" elif action == "cycle": cmd = "echo \"y\"|" + cmd cmd += " powercycle" if action in ["on", "off", "cycle"] and "--boot-option" in options: cmd += options["--boot-option"] # --use-sudo / -d if "--use-sudo" in options: cmd = options["--sudo-path"] + " " + cmd return cmd def define_new_opts(): all_opt["boot_option"] = { "getopt" : "b:", "longopt" : "boot-option", "help" : "-b, --boot-option=[option] " "Change the default boot behavior of the machine. (pxe|hd|hdsafe|cd|diag)", "required" : "0", "shortdesc" : "Change the default boot behavior of the machine.", "choices" : ["pxe", "hd", "hdsafe", "cd", "diag"], "order" : 1 } all_opt["amttool_path"] = { "getopt" : ":", "longopt" : "amttool-path", "help" : "--amttool-path=[path] Path to amttool binary", "required" : "0", "shortdesc" : "Path to amttool binary", "default" : "@AMTTOOL_PATH@", "order": 200 } def main(): atexit.register(atexit_handler) device_opt = ["ipaddr", "no_login", "passwd", "boot_option", "no_port", "sudo", "amttool_path", "method"] define_new_opts() all_opt["ipport"]["default"] = "16994" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for AMT" docs["longdesc"] = "fence_amt is an I/O Fencing agent \ which can be used with Intel AMT. This agent calls support software amttool\ (http://www.kraxel.org/cgit/amtterm/)." docs["vendorurl"] = "http://www.intel.com/" show_docs(options, docs) run_delay(options) if not is_executable(options["--amttool-path"]): fail_usage("Amttool not found or not accessible") result = fence_action(None, options, set_power_status, get_power_status, None, reboot_cycle) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/amt_ws/fence_amt_ws.py b/fence/agents/amt_ws/fence_amt_ws.py index 5284a77a..b99f20ad 100755 --- a/fence/agents/amt_ws/fence_amt_ws.py +++ b/fence/agents/amt_ws/fence_amt_ws.py @@ -1,243 +1,236 @@ #!@PYTHON@ -tt # # Fence agent for Intel AMT (WS) based on code from the openstack/ironic project: # https://github.com/openstack/ironic/blob/master/ironic/drivers/modules/amt/power.py # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import sys import atexit import logging sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import run_delay, fail_usage, fail, EC_STATUS import pywsman from xml.etree import ElementTree - -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Fence agent for Intel AMT (WS)" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - POWER_ON='2' POWER_OFF='8' POWER_CYCLE='10' RET_SUCCESS = '0' CIM_PowerManagementService = ('http://schemas.dmtf.org/wbem/wscim/1/' 'cim-schema/2/CIM_PowerManagementService') CIM_ComputerSystem = ('http://schemas.dmtf.org/wbem/wscim/' '1/cim-schema/2/CIM_ComputerSystem') CIM_AssociatedPowerManagementService = ('http://schemas.dmtf.org/wbem/wscim/' '1/cim-schema/2/' 'CIM_AssociatedPowerManagementService') CIM_BootConfigSetting = ('http://schemas.dmtf.org/wbem/wscim/' '1/cim-schema/2/CIM_BootConfigSetting') CIM_BootSourceSetting = ('http://schemas.dmtf.org/wbem/wscim/' '1/cim-schema/2/CIM_BootSourceSetting') def xml_find(doc, namespace, item): if doc is None: return tree = ElementTree.fromstring(doc.root().string()) query = ('.//{%(namespace)s}%(item)s' % {'namespace': namespace, 'item': item}) return tree.find(query) def _generate_power_action_input(action): method_input = "RequestPowerStateChange_INPUT" address = 'http://schemas.xmlsoap.org/ws/2004/08/addressing' anonymous = ('http://schemas.xmlsoap.org/ws/2004/08/addressing/' 'role/anonymous') wsman = 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd' namespace = CIM_PowerManagementService doc = pywsman.XmlDoc(method_input) root = doc.root() root.set_ns(namespace) root.add(namespace, 'PowerState', action) child = root.add(namespace, 'ManagedElement', None) child.add(address, 'Address', anonymous) grand_child = child.add(address, 'ReferenceParameters', None) grand_child.add(wsman, 'ResourceURI', CIM_ComputerSystem) g_grand_child = grand_child.add(wsman, 'SelectorSet', None) g_g_grand_child = g_grand_child.add(wsman, 'Selector', 'ManagedSystem') g_g_grand_child.attr_add(wsman, 'Name', 'Name') return doc def get_power_status(_, options): client = pywsman.Client(options["--ip"], int(options["--ipport"]), \ '/wsman', 'http', 'admin', options["--password"]) namespace = CIM_AssociatedPowerManagementService client_options = pywsman.ClientOptions() doc = client.get(client_options, namespace) _SOAP_ENVELOPE = 'http://www.w3.org/2003/05/soap-envelope' item = 'Fault' fault = xml_find(doc, _SOAP_ENVELOPE, item) if fault is not None: logging.error("Failed to get power state for: %s port:%s", \ options["--ip"], options["--ipport"]) fail(EC_STATUS) item = "PowerState" try: power_state = xml_find(doc, namespace, item).text except AttributeError: logging.error("Failed to get power state for: %s port:%s", \ options["--ip"], options["--ipport"]) fail(EC_STATUS) if power_state == POWER_ON: return "on" elif power_state == POWER_OFF: return "off" else: fail(EC_STATUS) def set_power_status(_, options): client = pywsman.Client(options["--ip"], int(options["--ipport"]), \ '/wsman', 'http', 'admin', options["--password"]) method = 'RequestPowerStateChange' client_options = pywsman.ClientOptions() client_options.add_selector('Name', 'Intel(r) AMT Power Management Service') if options["--action"] == "on": target_state = POWER_ON elif options["--action"] == "off": target_state = POWER_OFF elif options["--action"] == "reboot": target_state = POWER_CYCLE if options["--action"] in ["on", "off", "reboot"] \ and "--boot-option" in options: set_boot_order(_, client, options) doc = _generate_power_action_input(target_state) client_doc = client.invoke(client_options, CIM_PowerManagementService, \ method, doc) item = "ReturnValue" return_value = xml_find(client_doc, CIM_PowerManagementService, item).text if return_value != RET_SUCCESS: logging.error("Failed to set power state: %s for: %s", \ options["--action"], options["--ip"]) fail(EC_STATUS) def set_boot_order(_, client, options): method_input = "ChangeBootOrder_INPUT" address = 'http://schemas.xmlsoap.org/ws/2004/08/addressing' anonymous = ('http://schemas.xmlsoap.org/ws/2004/08/addressing/' 'role/anonymous') wsman = 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd' namespace = CIM_BootConfigSetting if options["--boot-option"] == "pxe": device = "Intel(r) AMT: Force PXE Boot" elif options["--boot-option"] == "hd" or "hdsafe": device = "Intel(r) AMT: Force Hard-drive Boot" elif options["--boot-option"] == "cd": device = "Intel(r) AMT: Force CD/DVD Boot" elif options["--boot-option"] == "diag": device = "Intel(r) AMT: Force Diagnostic Boot" else: logging.error('Boot device: %s not supported.', \ options["--boot-option"]) return method = 'ChangeBootOrder' client_options = pywsman.ClientOptions() client_options.add_selector('InstanceID', \ 'Intel(r) AMT: Boot Configuration 0') doc = pywsman.XmlDoc(method_input) root = doc.root() root.set_ns(namespace) child = root.add(namespace, 'Source', None) child.add(address, 'Address', anonymous) grand_child = child.add(address, 'ReferenceParameters', None) grand_child.add(wsman, 'ResourceURI', CIM_BootSourceSetting) g_grand_child = grand_child.add(wsman, 'SelectorSet', None) g_g_grand_child = g_grand_child.add(wsman, 'Selector', device) g_g_grand_child.attr_add(wsman, 'Name', 'InstanceID') if options["--boot-option"] == "hdsafe": g_g_grand_child = g_grand_child.add(wsman, 'Selector', 'True') g_g_grand_child.attr_add(wsman, 'Name', 'UseSafeMode') client_doc = client.invoke(client_options, CIM_BootConfigSetting, \ method, doc) item = "ReturnValue" return_value = xml_find(client_doc, CIM_BootConfigSetting, item).text if return_value != RET_SUCCESS: logging.error("Failed to set boot device to: %s for: %s", \ options["--boot-option"], options["--ip"]) fail(EC_STATUS) def reboot_cycle(_, options): status = set_power_status(_, options) return not bool(status) def define_new_opts(): all_opt["boot_option"] = { "getopt" : "b:", "longopt" : "boot-option", "help" : "-b, --boot-option=[option] " "Change the default boot behavior of the\n" " machine." " (pxe|hd|hdsafe|cd|diag)", "required" : "0", "shortdesc" : "Change the default boot behavior of the machine.", "choices" : ["pxe", "hd", "hdsafe", "cd", "diag"], "order" : 1 } def main(): atexit.register(atexit_handler) device_opt = ["ipaddr", "no_login", "passwd", "boot_option", "no_port", "method"] define_new_opts() all_opt["ipport"]["default"] = "16992" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for AMT (WS)" docs["longdesc"] = "fence_amt_ws is an I/O Fencing agent \ which can be used with Intel AMT (WS). This agent requires \ the pywsman Python library which is included in OpenWSMAN. \ (http://openwsman.github.io/)." docs["vendorurl"] = "http://www.intel.com/" show_docs(options, docs) run_delay(options) result = fence_action(None, options, set_power_status, get_power_status, \ None, reboot_cycle) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/apc/fence_apc.py b/fence/agents/apc/fence_apc.py index 1e9c3761..24a5a423 100644 --- a/fence/agents/apc/fence_apc.py +++ b/fence/agents/apc/fence_apc.py @@ -1,266 +1,260 @@ #!@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, time 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 - # Fix for connection timed out issue in: # https://bugzilla.redhat.com/show_bug.cgi?id=1342584 TIMEDOUT_DELAY = 0.5 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 "--switch" not in options: fail_usage("Failed: You have to enter physical switch number") else: if "--switch" not in options: 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 "] + 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)) time.sleep(TIMEDOUT_DELAY) conn.send_eol("") if exp_result != 0: break conn.send(chr(0o3)) 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 "--switch" not in options: fail_usage("Failed: You have to enter physical switch number") else: if "--switch" not in options: 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 "] + options["--command-prompt"], int(options["--shell-timeout"])): time.sleep(TIMEDOUT_DELAY) 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 to cancel :", int(options["--shell-timeout"])) conn.send_eol("YES") conn.log_expect("Press to continue...", int(options["--power-timeout"])) time.sleep(TIMEDOUT_DELAY) conn.send_eol("") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) conn.send(chr(0o3)) 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 (("--plug" in options) == 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/fence/agents/apc_snmp/fence_apc_snmp.py b/fence/agents/apc_snmp/fence_apc_snmp.py index f93cfa84..f68c0cb6 100644 --- a/fence/agents/apc_snmp/fence_apc_snmp.py +++ b/fence/agents/apc_snmp/fence_apc_snmp.py @@ -1,224 +1,218 @@ #!@PYTHON@ -tt # The Following agent has been tested on: # - APC Switched Rack PDU - SNMP v1 # (MB:v3.7.0 PF:v2.7.0 PN:apc_hw02_aos_270.bin AF1:v2.7.3 # AN1:apc_hw02_aos_270.bin AF1:v2.7.3 AN1:apc_hw02_rpdu_273.bin MN:AP7930 HR:B2) # - APC Web/SNMP Management Card - SNMP v1 and v3 (noAuthNoPrivacy,authNoPrivacy, authPrivacy) # (MB:v3.8.6 PF:v3.5.8 PN:apc_hw02_aos_358.bin AF1:v3.5.7 # AN1:apc_hw02_aos_358.bin AF1:v3.5.7 AN1:apc_hw02_rpdu_357.bin MN:AP7900 HR:B2) # - APC Switched Rack PDU - SNMP v1 # (MB:v3.7.0 PF:v2.7.0 PN:apc_hw02_aos_270.bin AF1:v2.7.3 # AN1:apc_hw02_rpdu_273.bin MN:AP7951 HR:B2) # - Tripplite PDUMH20HVNET 12.04.0055 - SNMP v1, v2c, v3 import sys import atexit import logging sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="APC SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # oid defining fence device OID_SYS_OBJECT_ID = '.1.3.6.1.2.1.1.2.0' ### GLOBAL VARIABLES ### # Device - see ApcRPDU, ApcMSP, ApcMS, TripplitePDU device = None # Port ID port_id = None # Switch ID switch_id = None # Classes describing Device params class TripplitePDU(object): # Rack PDU status_oid = '.1.3.6.1.4.1.850.10.2.3.5.1.2.1.%d' control_oid = '.1.3.6.1.4.1.850.10.2.3.5.1.4.1.%d' outlet_table_oid = '.1.3.6.1.4.1.850.10.2.3.5.1.5' ident_str = "Tripplite" state_on = 2 state_off = 1 turn_on = 2 turn_off = 1 has_switches = False class ApcRPDU(object): # Rack PDU status_oid = '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.%d' control_oid = '.1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%d' outlet_table_oid = '.1.3.6.1.4.1.318.1.1.12.3.5.1.1.2' ident_str = "APC rPDU" state_on = 1 state_off = 2 turn_on = 1 turn_off = 2 has_switches = False class ApcMSP(object): # Master Switch+ status_oid = '.1.3.6.1.4.1.318.1.1.6.7.1.1.5.%d.1.%d' control_oid = '.1.3.6.1.4.1.318.1.1.6.5.1.1.5.%d.1.%d' outlet_table_oid = '.1.3.6.1.4.1.318.1.1.6.7.1.1.4' ident_str = "APC Master Switch+" state_on = 1 state_off = 2 turn_on = 1 turn_off = 3 has_switches = True class ApcMS(object): # Master Switch - seems oldest, but supported on every APC PDU status_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d' control_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d' outlet_table_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.4' ident_str = "APC Master Switch (fallback)" state_on = 1 state_off = 2 turn_on = 1 turn_off = 2 has_switches = False class ApcMS6(object): # Master Switch with 6.x firmware status_oid = '.1.3.6.1.4.1.318.1.1.4.4.2.1.3.%d' control_oid = '.1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.%d' outlet_table_oid = '1.3.6.1.4.1.318.1.1.4.4.2.1.4' ident_str = "APC Master Switch with firmware v6.x" state_on = 1 state_off = 2 turn_on = 1 turn_off = 2 has_switches = False ### FUNCTIONS ### def apc_set_device(conn): global device agents_dir = {'.1.3.6.1.4.1.318.1.3.4.5':ApcRPDU, '.1.3.6.1.4.1.318.1.3.4.4':ApcMSP, '.1.3.6.1.4.1.850.1':TripplitePDU, '.1.3.6.1.4.1.318.1.3.4.6':ApcMS6, None:ApcMS} # First resolve type of APC apc_type = conn.walk(OID_SYS_OBJECT_ID) if not ((len(apc_type) == 1) and (apc_type[0][1] in agents_dir)): apc_type = [[None, None]] device = agents_dir[apc_type[0][1]] logging.debug("Trying %s"%(device.ident_str)) def apc_resolv_port_id(conn, options): global port_id, switch_id if device == None: apc_set_device(conn) # Now we resolv port_id/switch_id if (options["--plug"].isdigit()) and ((not device.has_switches) or (options["--switch"].isdigit())): port_id = int(options["--plug"]) if device.has_switches: switch_id = int(options["--switch"]) else: table = conn.walk(device.outlet_table_oid, 30) for x in table: if x[1].strip('"') == options["--plug"]: t = x[0].split('.') if device.has_switches: port_id = int(t[len(t)-1]) switch_id = int(t[len(t)-3]) else: port_id = int(t[len(t)-1]) if port_id == None: fail_usage("Can't find port with name %s!"%(options["--plug"])) def get_power_status(conn, options): if port_id == None: apc_resolv_port_id(conn, options) oid = ((device.has_switches) and device.status_oid%(switch_id, port_id) or device.status_oid%(port_id)) (oid, status) = conn.get(oid) return status == str(device.state_on) and "on" or "off" def set_power_status(conn, options): if port_id == None: apc_resolv_port_id(conn, options) oid = ((device.has_switches) and device.control_oid%(switch_id, port_id) or device.control_oid%(port_id)) conn.set(oid, (options["--action"] == "on" and device.turn_on or device.turn_off)) def get_outlets_status(conn, options): result = {} if device == None: apc_set_device(conn) res_ports = conn.walk(device.outlet_table_oid, 30) for x in res_ports: t = x[0].split('.') port_num = ((device.has_switches) and "%s:%s"%(t[len(t)-3], t[len(t)-1]) or "%s"%(t[len(t)-1])) port_name = x[1].strip('"') port_status = "" result[port_num] = (port_name, port_status) return result # Main agent method def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) all_opt["snmp_version"]["default"] = "1" all_opt["community"]["default"] = "private" options = check_input(device_opt, process_input(device_opt)) ## Support for -n [switch]:[plug] notation that was used before if ("--plug" in options) and (-1 != options["--plug"].find(":")): (switch, plug) = options["--plug"].split(":", 1) if switch.isdigit() and plug.isdigit(): options["--switch"] = switch options["--plug"] = plug if "--switch" not in options: options["--switch"] = "1" docs = {} docs["shortdesc"] = "Fence agent for APC, Tripplite PDU over SNMP" docs["longdesc"] = "fence_apc_snmp is an I/O Fencing agent \ which can be used with the APC network power switch or Tripplite PDU devices.\ It logs into a device via SNMP and reboots a specified outlet. It supports \ SNMP v1, v2c, v3 with all combinations of authenticity/privacy settings." docs["vendorurl"] = "http://www.apc.com" docs["symlink"] = [("fence_tripplite_snmp", "Fence agent for Tripplife over SNMP")] show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/azure_arm/fence_azure_arm.py b/fence/agents/azure_arm/fence_azure_arm.py index 72caddfc..b3e800fe 100644 --- a/fence/agents/azure_arm/fence_azure_arm.py +++ b/fence/agents/azure_arm/fence_azure_arm.py @@ -1,131 +1,125 @@ #!@PYTHON@ -tt import sys, re, pexpect import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_TIMED_OUT, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -BUILD_DATE="" -REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved." -#END_VERSION_GENERATION - def get_nodes_list(compute_client, options): result = {} if compute_client: rgName = options["--resourceGroup"] vms = compute_client.virtual_machines.list(rgName) for vm in vms: result[vm.name] = ("", None) return result def get_power_status(compute_client, options): logging.info("getting power status for VM " + options["--plug"]) if compute_client: rgName = options["--resourceGroup"] vmName = options["--plug"] powerState = "unknown" vmStatus = compute_client.virtual_machines.get(rgName, vmName, "instanceView") for status in vmStatus.instance_view.statuses: if status.code.startswith("PowerState"): powerState = status.code break logging.info("Found power state of VM: " + powerState) if powerState == "PowerState/running": return "on" return "off" def set_power_status(compute_client, options): logging.info("setting power status for VM " + options["--plug"] + " to " + options["--action"]) if compute_client: rgName = options["--resourceGroup"] vmName = options["--plug"] if (options["--action"]=="off"): logging.info("Deallocating " + vmName + "in resource group " + rgName) compute_client.virtual_machines.deallocate(rgName, vmName) elif (options["--action"]=="on"): logging.info("Starting " + vmName + "in resource group " + rgName) compute_client.virtual_machines.start(rgName, vmName) def define_new_opts(): all_opt["resourceGroup"] = { "getopt" : ":", "longopt" : "resourceGroup", "help" : "--resourceGroup=[name] Name of the resource group", "shortdesc" : "Name of resource group.", "required" : "1", "order" : 2 } all_opt["tenantId"] = { "getopt" : ":", "longopt" : "tenantId", "help" : "--tenantId=[name] Id of the Azure Active Directory tenant", "shortdesc" : "Id of Azure Active Directory tenant.", "required" : "1", "order" : 3 } all_opt["subscriptionId"] = { "getopt" : ":", "longopt" : "subscriptionId", "help" : "--subscriptionId=[name] Id of the Azure subscription", "shortdesc" : "Id of the Azure subscription.", "required" : "1", "order" : 4 } # Main agent method def main(): compute_client = None device_opt = ["resourceGroup", "login", "passwd", "tenantId", "subscriptionId","port"] atexit.register(atexit_handler) define_new_opts() options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Azure Resource Manager" docs["longdesc"] = "Used to deallocate virtual machines and to report power state of virtual machines running in Azure" docs["vendorurl"] = "http://www.microsoft.com" show_docs(options, docs) run_delay(options) try: from azure.common.credentials import ServicePrincipalCredentials from azure.mgmt.compute import ComputeManagementClient tenantid = options["--tenantId"] servicePrincipal = options["--username"] spPassword = options["--password"] subscriptionId = options["--subscriptionId"] credentials = ServicePrincipalCredentials( client_id = servicePrincipal, secret = spPassword, tenant = tenantid ) compute_client = ComputeManagementClient( credentials, subscriptionId ) except ImportError: fail_usage("Azure Resource Manager Pyhton SDK not found or not accessible") # Operate the fencing device result = fence_action(compute_client, options, set_power_status, get_power_status, get_nodes_list) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/bladecenter/fence_bladecenter.py b/fence/agents/bladecenter/fence_bladecenter.py index b45f3157..d670367f 100644 --- a/fence/agents/bladecenter/fence_bladecenter.py +++ b/fence/agents/bladecenter/fence_bladecenter.py @@ -1,111 +1,105 @@ #!@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 "--missing-as-off" in options: 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 "--missing-as-off" in options: 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/fence/agents/brocade/fence_brocade.py b/fence/agents/brocade/fence_brocade.py index 4cf039a9..9f413db6 100644 --- a/fence/agents/brocade/fence_brocade.py +++ b/fence/agents/brocade/fence_brocade.py @@ -1,78 +1,72 @@ #!@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/fence/agents/cisco_mds/fence_cisco_mds.py b/fence/agents/cisco_mds/fence_cisco_mds.py index cb3c9605..fbb876a9 100644 --- a/fence/agents/cisco_mds/fence_cisco_mds.py +++ b/fence/agents/cisco_mds/fence_cisco_mds.py @@ -1,100 +1,94 @@ #!@PYTHON@ -tt # The Following agent has been tested on: # - Cisco MDS UROS 9134 FC (1 Slot) Chassis ("1/2/4 10 Gbps FC/Supervisor-2") Motorola, e500v2 # with BIOS 1.0.16, kickstart 4.1(1c), system 4.1(1c) # - Cisco MDS 9124 (1 Slot) Chassis ("1/2/4 Gbps FC/Supervisor-2") Motorola, e500 # with BIOS 1.0.16, kickstart 4.1(1c), system 4.1(1c) import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, array_to_dict from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Cisco MDS 9xxx SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # Cisco admin status PORT_ADMIN_STATUS_OID = ".1.3.6.1.2.1.75.1.2.2.1.1" # IF-MIB trees for alias, status and port ALIASES_OID = ".1.3.6.1.2.1.31.1.1.1.18" PORTS_OID = ".1.3.6.1.2.1.2.2.1.2" ### GLOBAL VARIABLES ### # OID converted from fc port name (fc(x)/(y)) PORT_OID = "" ### FUNCTIONS ### # Convert cisco port name (fc(x)/(y)) to OID def cisco_port2oid(port): port = port.lower() nums = re.match(r'^fc(\d+)/(\d+)$', port) if nums and len(nums.groups()) == 2: return "%s.%d.%d"% (PORT_ADMIN_STATUS_OID, int(nums.group(1))+21, int(nums.group(2))-1) else: fail_usage("Mangled port number: %s"%(port)) def get_power_status(conn, options): (_, status) = conn.get(PORT_OID) return status == "1" and "on" or "off" def set_power_status(conn, options): conn.set(PORT_OID, (options["--action"] == "on" and 1 or 2)) def get_outlets_status(conn, options): result = {} res_fc = conn.walk(PORTS_OID, 30) res_aliases = array_to_dict(conn.walk(ALIASES_OID, 30)) fc_re = re.compile(r'^"fc\d+/\d+"$') for x in res_fc: if fc_re.match(x[1]): port_num = x[0].split('.')[-1] port_name = x[1].strip('"') port_alias = (port_num in res_aliases and res_aliases[port_num].strip('"') or "") port_status = "" result[port_name] = (port_alias, port_status) return result # Main agent method def main(): global PORT_OID device_opt = ["fabric_fencing", "ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Cisco MDS" docs["longdesc"] = "fence_cisco_mds is an I/O Fencing agent \ which can be used with any Cisco MDS 9000 series with SNMP enabled device." docs["vendorurl"] = "http://www.cisco.com" show_docs(options, docs) if not options["--action"] in ["list", "monitor"]: PORT_OID = cisco_port2oid(options["--plug"]) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/cisco_ucs/fence_cisco_ucs.py b/fence/agents/cisco_ucs/fence_cisco_ucs.py index 7102c445..d509b3e0 100644 --- a/fence/agents/cisco_ucs/fence_cisco_ucs.py +++ b/fence/agents/cisco_ucs/fence_cisco_ucs.py @@ -1,202 +1,196 @@ #!@PYTHON@ -tt import sys, re import pycurl, io import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_STATUS, EC_LOGIN_DENIED, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="New Cisco UCS Agent - test release on steroids" -REDHAT_COPYRIGHT="" -BUILD_DATE="March, 2008" -#END_VERSION_GENERATION - RE_COOKIE = re.compile("", int(options["--shell-timeout"])) result = RE_GET_PNDN.search(res) if result == None: pndn = "" else: pndn = result.group(1) if pndn.strip() == "": if "--missing-as-off" in options: return "off" else: fail(EC_STATUS) res = send_command(options, "", int(options["--shell-timeout"])) result = RE_GET_PRESENCE.search(res) if result == None: fail(EC_STATUS) else: presence_status = result.group(1) if presence_status in ["missing", "mismatch"]: return "off" else: result = RE_GET_OPERPOWER.search(res) if result == None: fail(EC_STATUS) else: power_status = result.group(1) if power_status == "on": return "on" else: return "off" def set_power_status(conn, options): del conn action = { 'on' : "admin-up", 'off' : "admin-down" }[options["--action"]] send_command(options, "" + "" + "" + "", int(options["--shell-timeout"])) return def get_list(conn, options): del conn outlets = {} try: res = send_command(options, "", int(options["--shell-timeout"])) lines = res.split("", int(options_global["--shell-timeout"])) except Exception: pass def main(): global options_global device_opt = ["ipaddr", "login", "passwd", "ssl", "notls", "port", "web", "suborg", "missing_as_off"] atexit.register(atexit_handler) atexit.register(logout) define_new_opts() options_global = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Cisco UCS" docs["longdesc"] = "fence_cisco_ucs is an I/O Fencing agent which can be \ used with Cisco UCS to fence machines." docs["vendorurl"] = "http://www.cisco.com" show_docs(options_global, docs) run_delay(options_global) ### Login try: res = send_command(options_global, "", int(options_global["--login-timeout"])) result = RE_COOKIE.search(res) if result == None: ## Cookie is absenting in response fail(EC_LOGIN_DENIED) except Exception: fail(EC_LOGIN_DENIED) options_global["cookie"] = result.group(1) ## ## Modify suborg to format /suborg if options_global["--suborg"] != "": options_global["--suborg"] = "/" + options_global["--suborg"].lstrip("/").rstrip("/") ## ## Fence operations #### result = fence_action(None, options_global, set_power_status, get_power_status, get_list) ## Logout is done every time at atexit phase sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/compute/fence_compute.py b/fence/agents/compute/fence_compute.py index 5ccd0baf..5f9fdd01 100644 --- a/fence/agents/compute/fence_compute.py +++ b/fence/agents/compute/fence_compute.py @@ -1,474 +1,468 @@ #!@PYTHON@ -tt import sys import time import atexit import logging import requests.exceptions sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, is_executable, run_command, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="4.0.11" -BUILD_DATE="(built Wed Nov 12 06:33:38 EST 2014)" -REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved." -#END_VERSION_GENERATION - override_status = "" nova = None EVACUABLE_TAG = "evacuable" TRUE_TAGS = ['true'] def get_power_status(_, options): global override_status status = "unknown" logging.debug("get action: " + options["--action"]) if len(override_status): logging.debug("Pretending we're " + override_status) return override_status if nova: try: services = nova.services.list(host=options["--plug"]) for service in services: logging.debug("Status of %s is %s" % (service.binary, service.state)) if service.binary == "nova-compute": if service.state == "up": status = "on" elif service.state == "down": status = "off" else: logging.debug("Unknown status detected from nova: " + service.state) break except requests.exception.ConnectionError as err: logging.warning("Nova connection failed: " + str(err)) return status # NOTE(sbauza); We mimic the host-evacuate module since it's only a contrib # module which is not stable def _server_evacuate(server, on_shared_storage): success = False error_message = "" try: logging.debug("Resurrecting instance: %s" % server) (response, dictionary) = nova.servers.evacuate(server=server, on_shared_storage=on_shared_storage) if response == None: error_message = "No response while evacuating instance" elif response.status_code == 200: success = True error_message = response.reason else: error_message = response.reason except Exception as e: error_message = "Error while evacuating instance: %s" % e return { "uuid": server, "accepted": success, "reason": error_message, } def _is_server_evacuable(server, evac_flavors, evac_images): if server.flavor.get('id') in evac_flavors: return True if server.image.get('id') in evac_images: return True logging.debug("Instance %s is not evacuable" % server.image.get('id')) return False def _get_evacuable_flavors(): result = [] flavors = nova.flavors.list() # Since the detailed view for all flavors doesn't provide the extra specs, # we need to call each of the flavor to get them. for flavor in flavors: tag = flavor.get_keys().get(EVACUABLE_TAG) if tag and tag.strip().lower() in TRUE_TAGS: result.append(flavor.id) return result def _get_evacuable_images(): result = [] images = nova.images.list(detailed=True) for image in images: if hasattr(image, 'metadata'): tag = image.metadata.get(EVACUABLE_TAG) if tag and tag.strip().lower() in TRUE_TAGS: result.append(image.id) return result def _host_evacuate(options): result = True images = _get_evacuable_images() flavors = _get_evacuable_flavors() servers = nova.servers.list(search_opts={'host': options["--plug"], 'all_tenants': 1 }) if options["--instance-filtering"] == "False": logging.debug("Not evacuating anything") evacuables = [] elif len(flavors) or len(images): logging.debug("Filtering images and flavors: %s %s" % (repr(flavors), repr(images))) # Identify all evacuable servers logging.debug("Checking %s" % repr(servers)) evacuables = [server for server in servers if _is_server_evacuable(server, flavors, images)] logging.debug("Evacuating %s" % repr(evacuables)) else: logging.debug("Evacuating all images and flavors") evacuables = servers if options["--no-shared-storage"] != "False": on_shared_storage = False else: on_shared_storage = True for server in evacuables: logging.debug("Processing %s" % server) if hasattr(server, 'id'): response = _server_evacuate(server.id, on_shared_storage) if response["accepted"]: logging.debug("Evacuated %s from %s: %s" % (response["uuid"], options["--plug"], response["reason"])) else: logging.error("Evacuation of %s on %s failed: %s" % (response["uuid"], options["--plug"], response["reason"])) result = False else: logging.error("Could not evacuate instance: %s" % server.to_dict()) # Should a malformed instance result in a failed evacuation? # result = False return result def set_attrd_status(host, status, options): logging.debug("Setting fencing status for %s to %s" % (host, status)) run_command(options, "attrd_updater -p -n evacuate -Q -N %s -U %s" % (host, status)) def set_power_status(_, options): global override_status override_status = "" logging.debug("set action: " + options["--action"]) if not nova: return if options["--action"] == "on": if get_power_status(_, options) != "on": # Forcing the service back up in case it was disabled nova.services.enable(options["--plug"], 'nova-compute') try: # Forcing the host back up nova.services.force_down( options["--plug"], "nova-compute", force_down=False) except Exception as e: # In theory, if force_down=False fails, that's for the exact # same possible reasons that below with force_down=True # eg. either an incompatible version or an old client. # Since it's about forcing back to a default value, there is # no real worries to just consider it's still okay even if the # command failed logging.info("Exception from attempt to force " "host back up via nova API: " "%s: %s" % (e.__class__.__name__, e)) else: # Pretend we're 'on' so that the fencing library doesn't loop forever waiting for the node to boot override_status = "on" return try: nova.services.force_down( options["--plug"], "nova-compute", force_down=True) except Exception as e: # Something went wrong when we tried to force the host down. # That could come from either an incompatible API version # eg. UnsupportedVersion or VersionNotFoundForAPIMethod # or because novaclient is old and doesn't include force_down yet # eg. AttributeError # In that case, fallbacking to wait for Nova to catch the right state. logging.error("Exception from attempt to force host down via nova API: " "%s: %s" % (e.__class__.__name__, e)) # need to wait for nova to update its internal status or we # cannot call host-evacuate while get_power_status(_, options) != "off": # Loop forever if need be. # # Some callers (such as Pacemaker) will have a timer # running and kill us if necessary logging.debug("Waiting for nova to update its internal state for %s" % options["--plug"]) time.sleep(1) if not _host_evacuate(options): sys.exit(1) return def fix_domain(options): domains = {} last_domain = None if nova: # Find it in nova hypervisors = nova.hypervisors.list() for hypervisor in hypervisors: shorthost = hypervisor.hypervisor_hostname.split('.')[0] if shorthost == hypervisor.hypervisor_hostname: # Nova is not using FQDN calculated = "" else: # Compute nodes are named as FQDN, strip off the hostname calculated = hypervisor.hypervisor_hostname.replace(shorthost+".", "") domains[calculated] = shorthost if calculated == last_domain: # Avoid complaining for each compute node with the same name # One hopes they don't appear interleaved as A.com B.com A.com B.com logging.debug("Calculated the same domain from: %s" % hypervisor.hypervisor_hostname) elif "--domain" in options and options["--domain"] == calculated: # Supplied domain name is valid return elif "--domain" in options: # Warn in case nova isn't available at some point logging.warning("Supplied domain '%s' does not match the one calculated from: %s" % (options["--domain"], hypervisor.hypervisor_hostname)) last_domain = calculated if len(domains) == 0 and "--domain" not in options: logging.error("Could not calculate the domain names used by compute nodes in nova") elif len(domains) == 1 and "--domain" not in options: options["--domain"] = last_domain return options["--domain"] elif len(domains) == 1: logging.error("Overriding supplied domain '%s' does not match the one calculated from: %s" % (options["--domain"], hypervisor.hypervisor_hostname)) options["--domain"] = last_domain return options["--domain"] elif len(domains) > 1: logging.error("The supplied domain '%s' did not match any used inside nova: %s" % (options["--domain"], repr(domains))) sys.exit(1) return None def fix_plug_name(options): if options["--action"] == "list": return if "--plug" not in options: return calculated = fix_domain(options) short_plug = options["--plug"].split('.')[0] logging.debug("Checking target '%s' against calculated domain '%s'"% (options["--plug"], options["--domain"])) if "--domain" not in options: # Nothing supplied and nova not available... what to do... nothing return elif options["--domain"] == "": # Ensure any domain is stripped off since nova isn't using FQDN options["--plug"] = short_plug elif options["--domain"] in options["--plug"]: # Plug already contains the domain, don't re-add return else: # Add the domain to the plug options["--plug"] = short_plug + "." + options["--domain"] def get_plugs_list(_, options): result = {} if nova: hypervisors = nova.hypervisors.list() for hypervisor in hypervisors: longhost = hypervisor.hypervisor_hostname shorthost = longhost.split('.')[0] result[longhost] = ("", None) result[shorthost] = ("", None) return result def create_nova_connection(options): global nova try: from novaclient import client from novaclient.exceptions import NotAcceptable except ImportError: fail_usage("Nova not found or not accessible") versions = [ "2.11", "2" ] for version in versions: nova = client.Client(version, options["--username"], options["--password"], options["--tenant-name"], options["--auth-url"], insecure=options["--insecure"], region_name=options["--region-name"], endpoint_type=options["--endpoint-type"], http_log_debug=options.has_key("--verbose")) try: nova.hypervisors.list() return except NotAcceptable as e: logging.warning(e) except Exception as e: logging.warning("Nova connection failed. %s: %s" % (e.__class__.__name__, e)) logging.warning("Couldn't obtain a supported connection to nova, tried: %s\n" % repr(versions)) def define_new_opts(): all_opt["endpoint-type"] = { "getopt" : "e:", "longopt" : "endpoint-type", "help" : "-e, --endpoint-type=[endpoint] Nova Endpoint type (publicURL, internalURL, adminURL)", "required" : "0", "shortdesc" : "Nova Endpoint type", "default" : "internalURL", "order": 1, } all_opt["tenant-name"] = { "getopt" : "t:", "longopt" : "tenant-name", "help" : "-t, --tenant-name=[tenant] Keystone Admin Tenant", "required" : "0", "shortdesc" : "Keystone Admin Tenant", "default" : "", "order": 1, } all_opt["auth-url"] = { "getopt" : "k:", "longopt" : "auth-url", "help" : "-k, --auth-url=[url] Keystone Admin Auth URL", "required" : "0", "shortdesc" : "Keystone Admin Auth URL", "default" : "", "order": 1, } all_opt["region-name"] = { "getopt" : "", "longopt" : "region-name", "help" : "--region-name=[region] Region Name", "required" : "0", "shortdesc" : "Region Name", "default" : "", "order": 1, } all_opt["insecure"] = { "getopt" : "", "longopt" : "insecure", "help" : "--insecure Explicitly allow agent to perform \"insecure\" TLS (https) requests", "required" : "0", "shortdesc" : "Allow Insecure TLS Requests", "default" : "False", "order": 2, } all_opt["domain"] = { "getopt" : "d:", "longopt" : "domain", "help" : "-d, --domain=[string] DNS domain in which hosts live, useful when the cluster uses short names and nova uses FQDN", "required" : "0", "shortdesc" : "DNS domain in which hosts live", "order": 5, } all_opt["record-only"] = { "getopt" : "r:", "longopt" : "record-only", "help" : "--record-only Record the target as needing evacuation but as yet do not intiate it", "required" : "0", "shortdesc" : "Only record the target as needing evacuation", "default" : "False", "order": 5, } all_opt["instance-filtering"] = { "getopt" : "", "longopt" : "instance-filtering", "help" : "--instance-filtering Allow instances created from images and flavors with evacuable=true to be evacuated (or all if no images/flavors have been tagged)", "required" : "0", "shortdesc" : "Allow instances to be evacuated", "default" : "True", "order": 5, } all_opt["no-shared-storage"] = { "getopt" : "", "longopt" : "no-shared-storage", "help" : "--no-shared-storage Disable functionality for shared storage", "required" : "0", "shortdesc" : "Disable functionality for dealing with shared storage", "default" : "False", "order": 5, } def main(): global override_status atexit.register(atexit_handler) device_opt = ["login", "passwd", "tenant-name", "auth-url", "fabric_fencing", "on_target", "no_login", "no_password", "port", "domain", "no-shared-storage", "endpoint-type", "record-only", "instance-filtering", "insecure", "region-name"] define_new_opts() all_opt["shell_timeout"]["default"] = "180" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for the automatic resurrection of OpenStack compute instances" docs["longdesc"] = "Used to tell Nova that compute nodes are down and to reschedule flagged instances" docs["vendorurl"] = "" show_docs(options, docs) if options["--record-only"] in [ "2", "Disabled", "disabled" ]: sys.exit(0) run_delay(options) create_nova_connection(options) fix_plug_name(options) if options["--record-only"] in [ "1", "True", "true", "Yes", "yes"]: if options["--action"] == "on": set_attrd_status(options["--plug"], "no", options) sys.exit(0) elif options["--action"] in ["off", "reboot"]: set_attrd_status(options["--plug"], "yes", options) sys.exit(0) elif options["--action"] in ["monitor", "status"]: sys.exit(0) if options["--action"] in ["off", "reboot"]: # Pretend we're 'on' so that the fencing library will always call set_power_status(off) override_status = "on" if options["--action"] == "on": # Pretend we're 'off' so that the fencing library will always call set_power_status(on) override_status = "off" result = fence_action(None, options, set_power_status, get_power_status, get_plugs_list, None) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/docker/fence_docker.py b/fence/agents/docker/fence_docker.py index 2bfa5eae..b2921016 100644 --- a/fence/agents/docker/fence_docker.py +++ b/fence/agents/docker/fence_docker.py @@ -1,164 +1,158 @@ #!@PYTHON@ -tt import atexit import sys import io import logging import pycurl import json sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import fail_usage, all_opt, fence_action, atexit_handler, check_input, process_input, show_docs, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION = "" -REDHAT_COPYRIGHT = "" -BUILD_DATE = "" -#END_VERSION_GENERATION - def get_power_status(conn, options): del conn status = send_cmd(options, "containers/%s/json" % options["--plug"]) if status is None: return None return "on" if status["State"]["Running"] else "off" def set_power_status(conn, options): del conn if options["--action"] == "on": send_cmd(options, "containers/%s/start" % options["--plug"], True) else: send_cmd(options, "containers/%s/kill" % options["--plug"], True) return def reboot_cycle(conn, options): del conn send_cmd(options, "containers/%s/restart" % options["--plug"], True) return get_power_status(conn, options) def get_list(conn, options): del conn output = send_cmd(options, "containers/json?all=1") containers = {} for container in output: containers[container["Id"]] = (container["Names"][0], {True:"off", False: "on"}[container["Status"][:4].lower() == "exit"]) return containers def send_cmd(options, cmd, post = False): url = "http%s://%s:%s/v%s/%s" % ("s" if "--ssl" in options else "", options["--ip"], options["--ipport"], options["--api-version"], cmd) conn = pycurl.Curl() output_buffer = io.BytesIO() if logging.getLogger().getEffectiveLevel() < logging.WARNING: conn.setopt(pycurl.VERBOSE, True) conn.setopt(pycurl.HTTPGET, 1) conn.setopt(pycurl.URL, url.encode("ascii")) if post: conn.setopt(pycurl.POST, 1) conn.setopt(pycurl.POSTFIELDSIZE, 0) conn.setopt(pycurl.WRITEFUNCTION, output_buffer.write) conn.setopt(pycurl.TIMEOUT, int(options["--shell-timeout"])) if "--ssl" in options: if not (set(("--tlscert", "--tlskey", "--tlscacert")) <= set(options)): fail_usage("Failed. If --ssl option is used, You have to also \ specify: --tlscert, --tlskey and --tlscacert") conn.setopt(pycurl.SSL_VERIFYPEER, 1) conn.setopt(pycurl.SSLCERT, options["--tlscert"]) conn.setopt(pycurl.SSLKEY, options["--tlskey"]) conn.setopt(pycurl.CAINFO, options["--tlscacert"]) else: conn.setopt(pycurl.SSL_VERIFYPEER, 0) conn.setopt(pycurl.SSL_VERIFYHOST, 0) logging.debug("URL: " + url) try: conn.perform() result = output_buffer.getvalue().decode() return_code = conn.getinfo(pycurl.RESPONSE_CODE) logging.debug("RESULT [" + str(return_code) + \ "]: " + result) conn.close() if return_code == 200: return json.loads(result) except pycurl.error: logging.error("Connection failed") except: if result is not None: logging.error(result) logging.error("Cannot parse json") return None def main(): atexit.register(atexit_handler) all_opt["tlscert"] = { "getopt" : ":", "longopt" : "tlscert", "help" : "--tlscert " "Path to client certificate for TLS authentication", "required" : "0", "shortdesc" : "Path to client certificate (PEM format) \ for TLS authentication. Required if --ssl option is used.", "order": 2 } all_opt["tlskey"] = { "getopt" : ":", "longopt" : "tlskey", "help" : "--tlskey " "Path to client key for TLS authentication", "required" : "0", "shortdesc" : "Path to client key (PEM format) for TLS \ authentication. Required if --ssl option is used.", "order": 2 } all_opt["tlscacert"] = { "getopt" : ":", "longopt" : "tlscacert", "help" : "--tlscacert " "Path to CA certificate for TLS authentication", "required" : "0", "shortdesc" : "Path to CA certificate (PEM format) for \ TLS authentication. Required if --ssl option is used.", "order": 2 } all_opt["api_version"] = { "getopt" : ":", "longopt" : "api-version", "help" : "--api-version " "Version of Docker Remote API (default: 1.11)", "required" : "0", "order" : 2, "default" : "1.11", } device_opt = ["ipaddr", "no_password", "no_login", "port", "method", "web", "tlscert", "tlskey", "tlscacert", "ssl", "api_version"] options = check_input(device_opt, process_input(device_opt)) docs = { } docs["shortdesc"] = "Fence agent for Docker" docs["longdesc"] = "fence_docker is I/O fencing agent which \ can be used with the Docker Engine containers. You can use this \ fence-agent without any authentication, or you can use TLS authentication \ (use --ssl option, more info about TLS authentication in docker: \ http://docs.docker.com/examples/https/)." docs["vendorurl"] = "www.docker.io" show_docs(options, docs) run_delay(options) result = fence_action(None, options, set_power_status, get_power_status, get_list, reboot_cycle) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/drac/fence_drac.py b/fence/agents/drac/fence_drac.py index f698b398..be3e9a58 100644 --- a/fence/agents/drac/fence_drac.py +++ b/fence/agents/drac/fence_drac.py @@ -1,68 +1,62 @@ #!@PYTHON@ -tt import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(conn, options): conn.send_eol("getmodinfo") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) status = re.compile(r"\s+(on|off)\s+", re.IGNORECASE).search(conn.before).group(1) return status.lower().strip() def set_power_status(conn, options): action = { 'on' : "powerup", 'off': "powerdown" }[options["--action"]] conn.send_eol("serveraction -d 0 " + action) conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) def main(): device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "telnet"] atexit.register(atexit_handler) opt = process_input(device_opt) if "--username" in opt: all_opt["cmd_prompt"]["default"] = ["\\[" + opt["--username"] + "\\]# "] else: all_opt["cmd_prompt"]["default"] = ["\\[" "username" + "\\]# "] options = check_input(device_opt, opt) docs = {} docs["shortdesc"] = "I/O Fencing agent for Dell DRAC IV" docs["longdesc"] = "fence_drac is an I/O Fencing agent which can be used with \ the Dell Remote Access Card (DRAC). This card provides remote access to controlling \ power to a server. It logs into the DRAC through the telnet interface of the card. By \ default, the telnet interface is not enabled. To enable the interface, you will need \ to use the racadm command in the racser-devel rpm available from Dell. \ \ To enable telnet on the DRAC: \ \ [root]# racadm config -g cfgSerial -o cfgSerialTelnetEnable 1 \ \ [root]# racadm racreset \ " docs["vendorurl"] = "http://www.dell.com" show_docs(options, docs) ## ## Operate the fencing device #### conn = fence_login(options) result = fence_action(conn, options, set_power_status, get_power_status, None) fence_logout(conn, "exit") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/drac5/fence_drac5.py b/fence/agents/drac5/fence_drac5.py index df113fea..648ecd91 100644 --- a/fence/agents/drac5/fence_drac5.py +++ b/fence/agents/drac5/fence_drac5.py @@ -1,153 +1,147 @@ #!@PYTHON@ -tt ##### ## ## The Following Agent Has Been Tested On: ## ## DRAC Version Firmware ## +-----------------+---------------------------+ ## DRAC 5 1.0 (Build 06.05.12) ## DRAC 5 1.21 (Build 07.05.04) ## ## @note: drac_version was removed ##### import sys, re, time import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="New Drac5 Agent - test release on steroids" -REDHAT_COPYRIGHT="" -BUILD_DATE="March, 2008" -#END_VERSION_GENERATION - def get_power_status(conn, options): if options["--drac-version"] == "DRAC MC": (_, status) = get_list_devices(conn, options)[options["--plug"]] else: if options["--drac-version"] == "DRAC CMC": conn.send_eol("racadm serveraction powerstatus -m " + options["--plug"]) elif options["--drac-version"] == "DRAC 5": conn.send_eol("racadm serveraction powerstatus") conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) status = re.compile(r"(^|: )(ON|OFF|Powering ON|Powering OFF)\s*$", re.IGNORECASE | re.MULTILINE).search(conn.before).group(2) if status.lower().strip() in ["on", "powering on", "powering off"]: return "on" else: return "off" def set_power_status(conn, options): action = { 'on' : "powerup", 'off': "powerdown" }[options["--action"]] if options["--drac-version"] == "DRAC CMC": conn.send_eol("racadm serveraction " + action + " -m " + options["--plug"]) elif options["--drac-version"] == "DRAC 5": conn.send_eol("racadm serveraction " + action) elif options["--drac-version"] == "DRAC MC": conn.send_eol("racadm serveraction -s " + options["--plug"] + " " + action) ## Fix issue with double-enter [CR/LF] ## We need to read two additional command prompts (one from get + one from set command) conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) if len(conn.before.strip()) == 0: options["eol"] = options["eol"][:-1] conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) def get_list_devices(conn, options): outlets = {} if options["--drac-version"] == "DRAC CMC": conn.send_eol("getmodinfo") list_re = re.compile(r"^([^\s]*?)\s+Present\s*(ON|OFF)\s*.*$") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) for line in conn.before.splitlines(): if list_re.search(line): outlets[list_re.search(line).group(1)] = ("", list_re.search(line).group(2)) elif options["--drac-version"] == "DRAC MC": conn.send_eol("getmodinfo") list_re = re.compile(r"^\s*([^\s]*)\s*---->\s*(.*?)\s+Present\s*(ON|OFF)\s*.*$") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) for line in conn.before.splitlines(): if list_re.search(line): outlets[list_re.search(line).group(2)] = ("", list_re.search(line).group(3)) elif options["--drac-version"] == "DRAC 5": ## DRAC 5 can be used only for one computer ## standard fence library can't handle correctly situation ## when some fence devices supported by fence agent ## works with 'list' and other should returns 'N/A' print("N/A") return outlets def define_new_opts(): all_opt["drac_version"] = { "getopt" : "d:", "longopt" : "drac-version", "help" : "-d, --drac-version=[version] Force DRAC version to use (DRAC 5|DRAC CMC|DRAC MC)", "required" : "0", "shortdesc" : "Force DRAC version to use", "choices" : ["DRAC CMC", "DRAC MC", "DRAC 5"], "order" : 1} def main(): device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \ "drac_version", "port", "no_port", "telnet"] atexit.register(atexit_handler) define_new_opts() all_opt["cmd_prompt"]["default"] = [r"\$", r"DRAC\/MC:"] options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Dell DRAC CMC/5" docs["longdesc"] = "fence_drac5 is an I/O Fencing agent \ which can be used with the Dell Remote Access Card v5 or CMC (DRAC). \ This device provides remote access to controlling power to a server. \ It logs into the DRAC through the telnet/ssh interface of the card. \ By default, the telnet interface is not enabled." docs["vendorurl"] = "http://www.dell.com" show_docs(options, docs) ## ## Operate the fencing device ###### conn = fence_login(options) if "--drac-version" not in options: ## autodetect from text issued by fence device if conn.before.find("CMC") >= 0: options["--drac-version"] = "DRAC CMC" elif conn.before.find("DRAC 5") >= 0: options["--drac-version"] = "DRAC 5" elif conn.after.find("DRAC/MC") >= 0: options["--drac-version"] = "DRAC MC" else: ## Assume this is DRAC 5 by default as we don't want to break anything options["--drac-version"] = "DRAC 5" if options["--drac-version"] in ["DRAC MC", "DRAC CMC"]: if "--plug" not in options and 0 == ["monitor", "list"].count(options["--action"]): fail_usage("Failed: You have to enter module name (-n)") result = fence_action(conn, options, set_power_status, get_power_status, get_list_devices) fence_logout(conn, "exit", 1) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/dummy/fence_dummy.py b/fence/agents/dummy/fence_dummy.py index e4f1589c..8fa2d9af 100644 --- a/fence/agents/dummy/fence_dummy.py +++ b/fence/agents/dummy/fence_dummy.py @@ -1,139 +1,133 @@ #!@PYTHON@ -tt import sys, random import logging import time import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="New Dummy Agent - test release on steroids" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - plug_status = "on" def get_power_status_file(conn, options): del conn try: status_file = open(options["--status-file"], 'r') except Exception: return "off" status = status_file.read() status_file.close() return status.lower() def set_power_status_file(conn, options): del conn if not (options["--action"] in ["on", "off"]): return status_file = open(options["--status-file"], 'w') status_file.write(options["--action"]) status_file.close() def get_power_status_fail(conn, options): outlets = get_outlets_fail(conn, options) if len(outlets) == 0 or "--plug" not in options: fail_usage("Failed: You have to enter existing machine!") else: return outlets[options["--plug"]][0] def set_power_status_fail(conn, options): global plug_status del conn plug_status = "unknown" if options["--action"] == "on": plug_status = "off" def get_outlets_fail(conn, options): del conn result = {} global plug_status if options["--action"] == "on": plug_status = "off" # This fake agent has no port data to list, so we have to make # something up for the list action. if options.get("--action", None) == "list": result["fake_port_1"] = [plug_status, "fake"] result["fake_port_2"] = [plug_status, "fake"] elif "--plug" not in options: fail_usage("Failed: You have to enter existing machine!") else: port = options["--plug"] result[port] = [plug_status, "fake"] return result def main(): device_opt = ["no_password", "status_file", "random_sleep_range", "type", "no_port"] atexit.register(atexit_handler) all_opt["status_file"] = { "getopt" : ":", "longopt" : "status-file", "help":"--status-file=[file] Name of file that holds current status", "required" : "0", "shortdesc" : "File with status", "default" : "/tmp/fence_dummy.status", "order": 1 } all_opt["random_sleep_range"] = { "getopt" : ":", "longopt" : "random_sleep_range", "help":"--random_sleep_range=[seconds] Issue a sleep between 1 and [seconds]", "required" : "0", "shortdesc" : "Issue a sleep between 1 and X seconds. Used for testing.", "order": 1 } all_opt["type"] = { "getopt" : ":", "longopt" : "type", "help":"--type=[type] Possible types are: file and fail", "required" : "0", "shortdesc" : "Type of the dummy fence agent", "default" : "file", "order": 1 } options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Dummy fence agent" docs["longdesc"] = "fence_dummy" docs["vendorurl"] = "http://www.example.com" show_docs(options, docs) run_delay(options) # random sleep for testing if "--random_sleep_range" in options: val = int(options["--random_sleep_range"]) ran = random.randint(1, val) logging.info("Random sleep for %d seconds\n", ran) time.sleep(ran) if options["--type"] == "fail": result = fence_action(None, options, set_power_status_fail, get_power_status_fail, get_outlets_fail) else: result = fence_action(None, options, set_power_status_file, get_power_status_file, None) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/eaton_snmp/fence_eaton_snmp.py b/fence/agents/eaton_snmp/fence_eaton_snmp.py index dfd540c0..9fbc0563 100644 --- a/fence/agents/eaton_snmp/fence_eaton_snmp.py +++ b/fence/agents/eaton_snmp/fence_eaton_snmp.py @@ -1,235 +1,229 @@ #!@PYTHON@ -tt # The Following agent has been tested on: # - Eaton ePDU Managed - SNMP v1 # EATON | Powerware ePDU model: Managed ePDU (PW104MA0UB99), firmware: 01.01.01 # - Eaton ePDU Switched - SNMP v1 # EATON | Powerware ePDU model: Switched ePDU (IPV3600), firmware: 2.0.K import sys import atexit import logging sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Eaton SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # oid defining fence device OID_SYS_OBJECT_ID = '.1.3.6.1.2.1.1.2.0' ### GLOBAL VARIABLES ### # Device - see EatonManagedePDU, EatonSwitchedePDU device = None # Port ID port_id = None # Switch ID switch_id = None # Did we issue a set before get (to adjust OID with Switched ePDU) after_set = False # Classes describing Device params # Managed ePDU class EatonManagedePDU(object): status_oid = '.1.3.6.1.4.1.534.6.6.6.1.2.2.1.3.%d' control_oid = '.1.3.6.1.4.1.534.6.6.6.1.2.2.1.3.%d' outlet_table_oid = '.1.3.6.1.4.1.534.6.6.6.1.2.2.1.1' ident_str = "Eaton Managed ePDU" state_off = 0 state_on = 1 state_cycling = 2 # FIXME: not usable with fence-agents turn_off = 0 turn_on = 1 turn_cycle = 2 # FIXME: not usable with fence-agents has_switches = False # Switched ePDU (Pulizzi 2) # NOTE: sysOID reports "20677.1", while data are actually at "20677.2" class EatonSwitchedePDU(object): status_oid = '.1.3.6.1.4.1.20677.2.6.3.%d.0' control_oid = '.1.3.6.1.4.1.20677.2.6.2.%d.0' outlet_table_oid = '.1.3.6.1.4.1.20677.2.6.3' ident_str = "Eaton Switched ePDU" state_off = 2 state_on = 1 state_cycling = 0 # Note: this status doesn't exist on this device turn_off = 2 turn_on = 1 turn_cycle = 3 # FIXME: not usable with fence-agents has_switches = False ### FUNCTIONS ### def eaton_set_device(conn): global device agents_dir = {'.1.3.6.1.4.1.534.6.6.6':EatonManagedePDU, '.1.3.6.1.4.1.20677.1':EatonSwitchedePDU, '.1.3.6.1.4.1.20677.2':EatonSwitchedePDU} # First resolve type of Eaton eaton_type = conn.walk(OID_SYS_OBJECT_ID) if not ((len(eaton_type) == 1) and (eaton_type[0][1] in agents_dir)): eaton_type = [[None, None]] device = agents_dir[eaton_type[0][1]] logging.debug("Trying %s"%(device.ident_str)) def eaton_resolv_port_id(conn, options): global port_id, switch_id if device == None: eaton_set_device(conn) # Restore the increment, that was removed in main for ePDU Managed if device.ident_str == "Eaton Switched ePDU": options["--plug"] = str(int(options["--plug"]) + 1) # Now we resolv port_id/switch_id if options["--plug"].isdigit() and ((not device.has_switches) or (options["--switch"].isdigit())): port_id = int(options["--plug"]) if device.has_switches: switch_id = int(options["--switch"]) else: table = conn.walk(device.outlet_table_oid, 30) for x in table: if x[1].strip('"') == options["--plug"]: t = x[0].split('.') if device.has_switches: port_id = int(t[len(t)-1]) switch_id = int(t[len(t)-3]) else: if device.ident_str == "Eaton Switched ePDU": port_id = int(t[len(t)-3]) else: port_id = int(t[len(t)-1]) if port_id == None: # Restore index offset, to provide a valid error output on Managed ePDU if device.ident_str != "Eaton Switched ePDU": options["--plug"] = str(int(options["--plug"]) + 1) fail_usage("Can't find port with name %s!"%(options["--plug"])) def get_power_status(conn, options): global port_id, after_set if port_id == None: eaton_resolv_port_id(conn, options) # Ajust OID for Switched ePDU when the get is after a set if after_set and device.ident_str == "Eaton Switched ePDU": port_id -= 1 after_set = False oid = ((device.has_switches) and device.status_oid%(switch_id, port_id) or device.status_oid%(port_id)) try: (oid, status) = conn.get(oid) if status == str(device.state_on): return "on" elif status == str(device.state_off): return "off" else: return None except Exception: return None def set_power_status(conn, options): global port_id, after_set after_set = True if port_id == None: eaton_resolv_port_id(conn, options) # Controls start at #2 on Switched ePDU, since #1 is the global command if device.ident_str == "Eaton Switched ePDU": port_id = int(port_id)+1 oid = ((device.has_switches) and device.control_oid%(switch_id, port_id) or device.control_oid%(port_id)) conn.set(oid, (options["--action"] == "on" and device.turn_on or device.turn_off)) def get_outlets_status(conn, options): outletCount = 0 result = {} if device == None: eaton_set_device(conn) res_ports = conn.walk(device.outlet_table_oid, 30) for x in res_ports: outletCount += 1 status = x[1] t = x[0].split('.') # Plug indexing start from zero, so we substract '1' from the # user's given plug number if device.ident_str == "Eaton Managed ePDU": port_num = str(int(((device.has_switches) and "%s:%s"%(t[len(t)-3], t[len(t)-1]) or "%s"%(t[len(t)-1]))) + 1) # Plug indexing start from zero, so we add '1' # for the user's exposed plug number port_name = str(int(x[1].strip('"')) + 1) port_status = "" result[port_num] = (port_name, port_status) else: # Switched ePDU do not propose an outletCount OID! # Invalid status (ie value == '0'), retrieved via the walk, # means the outlet is absent port_num = str(outletCount) port_name = str(outletCount) port_status = "" if status != '0': result[port_num] = (port_name, port_status) return result # Main agent method def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) all_opt["switch"]["default"] = 1 all_opt["power_wait"]["default"] = 2 all_opt["snmp_version"]["default"] = "1" all_opt["community"]["default"] = "private" options = check_input(device_opt, process_input(device_opt)) # Plug indexing start from zero on ePDU Managed, so we substract '1' from # the user's given plug number. # For Switched ePDU, we will add this back again later. if "--plug" in options and options["--plug"].isdigit(): options["--plug"] = str(int(options["--plug"]) - 1) docs = {} docs["shortdesc"] = "Fence agent for Eaton over SNMP" docs["longdesc"] = "fence_eaton_snmp is an I/O Fencing agent \ which can be used with the Eaton network power switch. It logs \ into a device via SNMP and reboots a specified outlet. It supports \ SNMP v1 and v3 with all combinations of authenticity/privacy settings." docs["vendorurl"] = "http://powerquality.eaton.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/emerson/fence_emerson.py b/fence/agents/emerson/fence_emerson.py index ed5b5ae5..2e65cda0 100644 --- a/fence/agents/emerson/fence_emerson.py +++ b/fence/agents/emerson/fence_emerson.py @@ -1,68 +1,62 @@ #!@PYTHON@ -tt import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Emerson SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### STATUSES_OID = ".1.3.6.1.4.1.476.1.42.3.8.50.20.1.95" CONTROL_OID = ".1.3.6.1.4.1.476.1.42.3.8.50.20.1.100" NAMES_OID = ".1.3.6.1.4.1.476.1.42.3.8.50.20.1.10" # Status constants returned as value from SNMP STATUS_DOWN = 1 STATUS_UP = 2 # Status constants to set as value to SNMP STATUS_SET_OFF = 0 STATUS_SET_ON = 1 def get_power_status(conn, options): (_, status) = conn.get("%s.%s"% (STATUSES_OID, options["--plug"])) return status == str(STATUS_UP) and "on" or "off" def set_power_status(conn, options): conn.set("%s.%s" % (CONTROL_OID, options["--plug"]), (options["--action"] == "on" and STATUS_SET_ON or STATUS_SET_OFF)) def get_outlets_status(conn, _): result = {} res_outlet = conn.walk(STATUSES_OID, 30) for outlet_info in res_outlet: port_num = ".".join(outlet_info[0].split('.')[-3:]) port_alias = conn.get("%s.%s"% (NAMES_OID, port_num))[1] port_status = (outlet_info[1] == str(STATUS_UP) and "on" or "off") result[port_num] = (port_alias, port_status) return result def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) all_opt["power_wait"]["default"] = "5" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Emerson over SNMP" docs["longdesc"] = "fence_emerson is an I/O Fencing agent \ which can be used with MPX and MPH2 managed rack PDU." docs["vendorurl"] = "http://www.emersonnetworkpower.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/eps/fence_eps.py b/fence/agents/eps/fence_eps.py index 65de6a0b..74c89b95 100644 --- a/fence/agents/eps/fence_eps.py +++ b/fence/agents/eps/fence_eps.py @@ -1,134 +1,128 @@ #!@PYTHON@ -tt # The Following Agent Has Been Tested On: # ePowerSwitch 8M+ version 1.0.0.4 import sys, re import base64, string, socket import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, EC_LOGIN_DENIED, EC_TIMED_OUT, run_delay if sys.version_info[0] > 2: import http.client as httplib else: import httplib -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="ePowerSwitch 8M+ (eps)" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - # Run command on EPS device. # @param options Device options # @param params HTTP GET parameters (without ?) def eps_run_command(options, params): try: # New http connection conn = httplib.HTTPConnection(options["--ip"]) request_str = "/"+options["--page"] if params != "": request_str += "?"+params logging.debug("GET %s\n", request_str) conn.putrequest('GET', request_str) if "--username" in options: if "--password" not in options: options["--password"] = "" # Default is empty password # String for Authorization header auth_str = 'Basic ' + string.strip(base64.encodestring(options["--username"]+':'+options["--password"])) logging.debug("Authorization: %s\n", auth_str) conn.putheader('Authorization', auth_str) conn.endheaders() response = conn.getresponse() logging.debug("%d %s\n", response.status, response.reason) #Response != OK -> couldn't login if response.status != 200: fail(EC_LOGIN_DENIED) result = response.read() logging.debug("%s \n", result) conn.close() except socket.timeout: fail(EC_TIMED_OUT) except socket.error: fail(EC_LOGIN_DENIED) return result def get_power_status(conn, options): del conn ret_val = eps_run_command(options, "") result = {} status = re.findall(r"p(\d{2})=(0|1)\s*\", ret_val.lower()) for out_num, out_stat in status: result[out_num] = ("", (out_stat == "1" and "on" or "off")) if not options["--action"] in ['monitor', 'list']: if not options["--plug"] in result: fail_usage("Failed: You have to enter existing physical plug!") else: return result[options["--plug"]][1] else: return result def set_power_status(conn, options): del conn eps_run_command(options, "P%s=%s"%(options["--plug"], (options["--action"] == "on" and "1" or "0"))) # Define new option def eps_define_new_opts(): all_opt["hidden_page"] = { "getopt" : "c:", "longopt" : "page", "help":"-c, --page=[page] Name of hidden page (default: hidden.htm)", "required" : "0", "shortdesc" : "Name of hidden page", "default" : "hidden.htm", "order": 1 } # Starting point of fence agent def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "hidden_page", "web"] atexit.register(atexit_handler) eps_define_new_opts() options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for ePowerSwitch" docs["longdesc"] = "fence_eps is an I/O Fencing agent \ which can be used with the ePowerSwitch 8M+ power switch to fence \ connected machines. Fence agent works ONLY on 8M+ device, because \ this is only one, which has support for hidden page feature. \ \n.TP\n\ Agent basically works by connecting to hidden page and pass \ appropriate arguments to GET request. This means, that hidden \ page feature must be enabled and properly configured." docs["vendorurl"] = "http://www.epowerswitch.com" show_docs(options, docs) run_delay(options) #Run fence action. Conn is None, beacause we always need open new http connection result = fence_action(None, options, set_power_status, get_power_status, get_power_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/hds_cb/fence_hds_cb.py b/fence/agents/hds_cb/fence_hds_cb.py index 51cf7176..375054cf 100755 --- a/fence/agents/hds_cb/fence_hds_cb.py +++ b/fence/agents/hds_cb/fence_hds_cb.py @@ -1,138 +1,132 @@ #!@PYTHON@ -tt ##### ## ## The Following Agent Has Been Tested On: ## ## Model Modle/Firmware ## +--------------------+---------------------------+ ## (1) Main application CB2000/A0300-E-6617 ## ##### import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="New Compute Blade 2000 Agent - test release on steroids" -REDHAT_COPYRIGHT="" -BUILD_DATE="November, 2012" -#END_VERSION_GENERATION - RE_STATUS_LINE = r"^([0-9]+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+).*$" def get_power_status(conn, options): #### Maybe should put a conn.log_expect here to make sure #### we have properly entered into the main menu conn.sendline("S") # Enter System Command Mode conn.log_expect("SVP>", int(options["--shell-timeout"])) conn.sendline("PC") # Enter partition control conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) result = {} # Status can now be obtained from the output of the PC # command. Line looks like the following: # "P Power Condition LID lamp Mode Auto power on" # "0 On Normal Off Basic Synchronized" # "1 On Normal Off Basic Synchronized" for line in conn.before.splitlines(): # populate the relevant fields based on regex partition = re.search(RE_STATUS_LINE, line) if partition != None: # find the blade number defined in args if partition.group(1) == options["--plug"]: result = partition.group(2).lower() # We must make sure we go back to the main menu as the # status is checked before any fencing operations are # executed. We could in theory save some time by staying in # the partition control, but the logic is a little cleaner # this way. conn.sendline("Q") # Back to system command mode conn.log_expect("SVP>", int(options["--shell-timeout"])) conn.sendline("EX") # Back to system console main menu conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) return result def set_power_status(conn, options): action = { 'on' : "P", 'off': "F", 'reboot' : "H", }[options["--action"]] conn.sendline("S") # Enter System Command Mode conn.log_expect("SVP>", int(options["--shell-timeout"])) conn.sendline("PC") # Enter partition control conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.sendline("P") # Enter power control menu conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.sendline(action) # Execute action from array above conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.sendline(options["--plug"]) # Select blade number from args conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.sendline("Y") # Confirm action conn.log_expect("Hit enter key.", int(options["--shell-timeout"])) conn.sendline("") # Press the any key conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.sendline("Q") # Quit back to partition control conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) conn.sendline("Q") # Quit back to system command mode conn.log_expect("SVP>", int(options["--shell-timeout"])) conn.sendline("EX") # Quit back to system console menu conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) def get_blades_list(conn, options): outlets = {} conn.sendline("S") # Enter System Command Mode conn.log_expect("SVP>", int(options["--shell-timeout"])) conn.sendline("PC") # Enter partition control conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) # Status can now be obtained from the output of the PC # command. Line looks like the following: # "P Power Condition LID lamp Mode Auto power on" # "0 On Normal Off Basic Synchronized" # "1 On Normal Off Basic Synchronized" for line in conn.before.splitlines(): partition = re.search(RE_STATUS_LINE, line) if partition != None: outlets[partition.group(1)] = (partition.group(2), "") conn.sendline("Q") # Quit back to system command mode conn.log_expect("SVP>", int(options["--shell-timeout"])) conn.sendline("EX") # Quit back to system console menu conn.log_expect(options["--command-prompt"], int(options["--shell-timeout"])) 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"] = "5" all_opt["cmd_prompt"]["default"] = [r"\) :"] options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Hitachi Compute Blade systems" docs["longdesc"] = "fence_hds_cb is an I/O Fencing agent \ which can be used with Hitachi Compute Blades with recent enough firmware that \ includes telnet support." docs["vendorurl"] = "http://www.hds.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_blades_list) fence_logout(conn, "X") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/hpblade/fence_hpblade.py b/fence/agents/hpblade/fence_hpblade.py index a9e712cc..b2cc94a3 100644 --- a/fence/agents/hpblade/fence_hpblade.py +++ b/fence/agents/hpblade/fence_hpblade.py @@ -1,140 +1,134 @@ #!@PYTHON@ -tt ##### ## ## The Following Agent Has Been Tested On: ## * HP BladeSystem c7000 Enclosure ## * HP Integrity Superdome X (BL920s) ##### import sys, re import pexpect import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_STATUS -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="4.0.11-HP" -BUILD_DATE="(built Mon Mar 30 08:31:24 EDT 2015)" -REDHAT_COPYRIGHT="Copyright (C) Red Hat, Inc. 2004-2010 All rights reserved." -#END_VERSION_GENERATION - def get_enclosure_type(conn, options): conn.send_eol("show enclosure info") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) type_re=re.compile(r"^\s*Enclosure Type: (\w+)(.*?)\s*$") enclosure="unknown" for line in conn.before.splitlines(): res = type_re.search(line) if res != None: enclosure=res.group(1) if enclosure == "unknown": fail(EC_GENERIC_ERROR) return enclosure.lower().strip() def get_power_status(conn, options): if options["enc_type"] == "superdome": cmd_send = "parstatus -M -p " + options["--plug"] powrestr = "^partition:\\d\\s+:\\w+\\s+/(\\w+)\\s.*$" else: cmd_send = "show server status " + options["--plug"] powrestr = "^\\s*Power: (.*?)\\s*$" conn.send_eol(cmd_send) conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) power_re = re.compile(powrestr) status = "unknown" for line in conn.before.splitlines(): res = power_re.search(line) if res != None: if options["enc_type"] == "superdome": if res.group(1) == "DOWN": status = "off" else: status = "on" else: status = res.group(1) if status == "unknown": if "--missing-as-off" in options: return "off" else: fail(EC_STATUS) return status.lower().strip() def set_power_status(conn, options): if options["enc_type"] == "superdome": dev="partition " else: dev="server " if options["--action"] == "on": conn.send_eol("poweron " + dev + options["--plug"]) elif options["--action"] == "off": conn.send_eol("poweroff " + dev + options["--plug"] + " force") conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) def get_instances_list(conn, options): outlets = {} if options["enc_type"] == "superdome": cmd_send = "parstatus -P -M" listrestr = "^partition:(\\d+)\\s+:\\w+\\s+/(\\w+)\\s+:OK.*?:(\\w+)\\s*$" else: cmd_send = "show server list" listrestr = "^\\s*(\\d+)\\s+(.*?)\\s+(.*?)\\s+OK\\s+(.*?)\\s+(.*?)\\s*$" conn.send_eol(cmd_send) conn.log_expect(options, options["--command-prompt"], int(options["--shell-timeout"])) list_re = re.compile(listrestr) for line in conn.before.splitlines(): res = list_re.search(line) if res != None: if options["enc_type"] == "superdome": outlets[res.group(1)] = (res.group(3), res.group(2).lower()) else: outlets[res.group(1)] = (res.group(2), res.group(4).lower()) return outlets def main(): device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", \ "port", "missing_as_off", "telnet"] atexit.register(atexit_handler) all_opt["cmd_prompt"]["default"] = ["c7000oa>"] all_opt["login_timeout"]["default"] = "10" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for HP BladeSystem" docs["longdesc"] = "fence_hpblade is an I/O Fencing agent \ which can be used with HP BladeSystem and HP Integrity Superdome X. \ It logs into the onboard administrator of an enclosure via telnet or \ ssh and uses the command line interface to power blades or partitions \ on or off." docs["vendorurl"] = "http://www.hp.com" show_docs(options, docs) ## ## Operate the fencing device ###### options["eol"] = "\n" conn = fence_login(options) options["enc_type"] = get_enclosure_type(conn, options) result = fence_action(conn, options, set_power_status, get_power_status, get_instances_list) fence_logout(conn, "exit") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ibmblade/fence_ibmblade.py b/fence/agents/ibmblade/fence_ibmblade.py index 11ba1698..d623fff3 100644 --- a/fence/agents/ibmblade/fence_ibmblade.py +++ b/fence/agents/ibmblade/fence_ibmblade.py @@ -1,78 +1,72 @@ #!@PYTHON@ -tt import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="IBM Blade SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # From fence_ibmblade.pl STATUSES_OID = ".1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.4" # remoteControlBladePowerState CONTROL_OID = ".1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.7" # powerOnOffBlade # Status constants returned as value from SNMP STATUS_DOWN = 0 STATUS_UP = 1 # Status constants to set as value to SNMP STATUS_SET_OFF = 0 STATUS_SET_ON = 1 ### FUNCTIONS ### def get_power_status(conn, options): (_, status) = conn.get("%s.%s"% (STATUSES_OID, options["--plug"])) return status == str(STATUS_UP) and "on" or "off" def set_power_status(conn, options): conn.set("%s.%s" % (CONTROL_OID, options["--plug"]), (options["--action"] == "on" and STATUS_SET_ON or STATUS_SET_OFF)) def get_outlets_status(conn, _): result = {} res_blades = conn.walk(STATUSES_OID, 30) for blade_info in res_blades: port_num = blade_info[0].split('.')[-1] port_alias = "" port_status = (blade_info[1] == str(STATUS_UP) and "on" or "off") result[port_num] = (port_alias, port_status) return result # Main agent method def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) all_opt["snmp_version"]["default"] = "1" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for IBM BladeCenter over SNMP" docs["longdesc"] = "fence_ibmblade is an I/O Fencing agent \ which can be used with IBM BladeCenter chassis. It issues SNMP Set \ request to BladeCenter chassis, rebooting, powering up or down \ the specified Blade Server." docs["vendorurl"] = "http://www.ibm.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ifmib/fence_ifmib.py b/fence/agents/ifmib/fence_ifmib.py index 963d3e93..d1191340 100644 --- a/fence/agents/ifmib/fence_ifmib.py +++ b/fence/agents/ifmib/fence_ifmib.py @@ -1,122 +1,116 @@ #!@PYTHON@ -tt # The Following agent has been tested on: # - Cisco MDS UROS 9134 FC (1 Slot) Chassis ("1/2/4 10 Gbps FC/Supervisor-2") Motorola, e500v2 # with BIOS 1.0.16, kickstart 4.1(1c), system 4.1(1c) # - Cisco MDS 9124 (1 Slot) Chassis ("1/2/4 Gbps FC/Supervisor-2") Motorola, e500 # with BIOS 1.0.16, kickstart 4.1(1c), system 4.1(1c) # - Partially with APC PDU (Network Management Card AOS v2.7.0, Rack PDU APP v2.7.3) # Only lance if is visible import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, array_to_dict from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="IF:MIB SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # IF-MIB trees for alias, status and port ALIASES_OID = ".1.3.6.1.2.1.31.1.1.1.18" PORTS_OID = ".1.3.6.1.2.1.2.2.1.2" STATUSES_OID = ".1.3.6.1.2.1.2.2.1.7" # Status constants returned as value from SNMP STATUS_UP = 1 STATUS_DOWN = 2 STATUS_TESTING = 3 ### GLOBAL VARIABLES ### # Port number converted from port name or index port_num = None ### FUNCTIONS ### # Convert port index or name to port index def port2index(conn, port): res = None if port.isdigit(): res = int(port) else: ports = conn.walk(PORTS_OID, 30) for x in ports: if x[1].strip('"') == port: res = int(x[0].split('.')[-1]) break if res == None: fail_usage("Can't find port with name %s!"%(port)) return res def get_power_status(conn, options): global port_num if port_num == None: port_num = port2index(conn, options["--plug"]) (_, status) = conn.get("%s.%d"%(STATUSES_OID, port_num)) return status == str(STATUS_UP) and "on" or "off" def set_power_status(conn, options): global port_num if port_num == None: port_num = port2index(conn, options["--plug"]) conn.set("%s.%d" % (STATUSES_OID, port_num), (options["--action"] == "on" and STATUS_UP or STATUS_DOWN)) def get_outlets_status(conn, options): result = {} res_fc = conn.walk(PORTS_OID, 30) res_aliases = array_to_dict(conn.walk(ALIASES_OID, 30)) for x in res_fc: port_number = x[0].split('.')[-1] port_name = x[1].strip('"') port_alias = (port_number in res_aliases and res_aliases[port_number].strip('"') or "") port_status = "" result[port_name] = (port_alias, port_status) return result # Main agent method def main(): device_opt = ["fabric_fencing", "ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) all_opt["snmp_version"]["default"] = "2c" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for IF MIB" docs["longdesc"] = "fence_ifmib is an I/O Fencing agent \ which can be used with any SNMP IF-MIB capable device. \ \n.P\n\ It was written with managed ethernet switches in mind, in order to \ fence iSCSI SAN connections. However, there are many devices that \ support the IF-MIB interface. The agent uses IF-MIB::ifAdminStatus \ to control the state of an interface." docs["vendorurl"] = "http://www.ietf.org/wg/concluded/ifmib.html" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ilo/fence_ilo.py b/fence/agents/ilo/fence_ilo.py index 36e2ba14..61245056 100644 --- a/fence/agents/ilo/fence_ilo.py +++ b/fence/agents/ilo/fence_ilo.py @@ -1,149 +1,143 @@ #!@PYTHON@ -tt ##### ## ## The Following Agent Has Been Tested On: ## ## iLO Version ## +---------------------------------------------+ ## iLO / firmware 1.91 / RIBCL 2.22 ## iLO2 / firmware 1.22 / RIBCL 2.22 ## iLO2 / firmware 1.50 / RIBCL 2.22 ##### import sys, re, pexpect import atexit from xml.sax.saxutils import quoteattr sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, EC_LOGIN_DENIED -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="New ILO Agent - test release on steroids" -REDHAT_COPYRIGHT="" -BUILD_DATE="March, 2008" -#END_VERSION_GENERATION - def get_power_status(conn, options): conn.send("\r\n") conn.send("\r\n") conn.send("\r\n") conn.log_expect("HOST_POWER=\"(.*?)\"", int(options["--power-timeout"])) status = conn.match.group(1) return status.lower().strip() def set_power_status(conn, options): conn.send("\r\n") conn.send("") if options.get("fw_processor", None) == "iLO2": if options["fw_version"] > 1.29: conn.send("\r\n") else: conn.send("\r\n") elif options["--ribcl-version"] < 2.21: conn.send("\r\n") else: if options["--action"] == "off": conn.send("\r\n") else: conn.send("\r\n") conn.send("\r\n") return def define_new_opts(): all_opt["ribcl"] = { "getopt" : "r:", "longopt" : "ribcl-version", "help" : "-r, --ribcl-version=[version] Force ribcl version to use", "required" : "0", "shortdesc" : "Force ribcl version to use", "order" : 1} def main(): device_opt = ["ipaddr", "login", "passwd", "ssl", "notls", "tls1.0", "ribcl"] atexit.register(atexit_handler) define_new_opts() all_opt["login_timeout"]["default"] = "10" all_opt["retry_on"]["default"] = "3" all_opt["ssl"]["default"] = "1" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for HP iLO" docs["longdesc"] = "fence_ilo is an I/O Fencing agent \ used for HP servers with the Integrated Light Out (iLO) PCI card.\ The agent opens an SSL connection to the iLO card. Once the SSL \ connection is established, the agent is able to communicate with \ the iLO card through an XML stream." docs["vendorurl"] = "http://www.hp.com" docs["symlink"] = [("fence_ilo2", "Fence agent for HP iLO2")] show_docs(options, docs) ## ## Login and get version number #### conn = fence_login(options) try: conn.send("\r\n") conn.log_expect(["", ""], int(options["--login-timeout"])) except pexpect.TIMEOUT: fail(EC_LOGIN_DENIED) except pexpect.EOF: if "--tls1.0" in options: fail(EC_LOGIN_DENIED) options["--tls1.0"] = "1" conn.close() conn = fence_login(options) try: conn.send("\r\n") conn.log_expect(["", ""], int(options["--login-timeout"])) except pexpect.TIMEOUT: fail(EC_LOGIN_DENIED) except pexpect.EOF: fail(EC_LOGIN_DENIED) try: version = re.compile("= 2: conn.send("\r\n") else: conn.send("\r\n") conn.send("\r\n") if options["--ribcl-version"] >= 2: conn.send("\r\n") conn.send("\r\n") conn.log_expect(r"", int(options["--shell-timeout"])) options["fw_version"] = float(re.compile(r"FIRMWARE_VERSION\s*=\s*\"(.*?)\"", re.IGNORECASE).search(conn.before).group(1)) options["fw_processor"] = re.compile(r"MANAGEMENT_PROCESSOR\s*=\s*\"(.*?)\"", re.IGNORECASE).search(conn.before).group(1) conn.send("\r\n") except pexpect.TIMEOUT: fail(EC_LOGIN_DENIED) except pexpect.EOF: fail(EC_LOGIN_DENIED) ## ## Fence operations #### result = fence_action(conn, options, set_power_status, get_power_status, None) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ilo_moonshot/fence_ilo_moonshot.py b/fence/agents/ilo_moonshot/fence_ilo_moonshot.py index c534947c..a066a9c9 100644 --- a/fence/agents/ilo_moonshot/fence_ilo_moonshot.py +++ b/fence/agents/ilo_moonshot/fence_ilo_moonshot.py @@ -1,69 +1,63 @@ #!@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/fence/agents/ilo_mp/fence_ilo_mp.py b/fence/agents/ilo_mp/fence_ilo_mp.py index f878c5c4..1ae4d3ed 100644 --- a/fence/agents/ilo_mp/fence_ilo_mp.py +++ b/fence/agents/ilo_mp/fence_ilo_mp.py @@ -1,64 +1,58 @@ #!@PYTHON@ -tt import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(conn, options): conn.send_eol("show /system1") re_state = re.compile('EnabledState=(.*)', re.IGNORECASE) conn.log_expect(re_state, int(options["--shell-timeout"])) status = conn.match.group(1).lower() if status.startswith("enabled"): return "on" else: return "off" def set_power_status(conn, options): if options["--action"] == "on": conn.send_eol("start /system1") else: conn.send_eol("stop -f /system1") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) return def main(): device_opt = ["ipaddr", "login", "passwd", "secure", "cmd_prompt", "telnet"] atexit.register(atexit_handler) all_opt["cmd_prompt"]["default"] = ["MP>", "hpiLO->"] all_opt["power_wait"]["default"] = 5 options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for HP iLO MP" docs["longdesc"] = "" docs["vendorurl"] = "http://www.hp.com" show_docs(options, docs) conn = fence_login(options) conn.send_eol("SMCLP") ## ## Fence operations #### result = fence_action(conn, options, set_power_status, get_power_status) fence_logout(conn, "exit") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ilo_ssh/fence_ilo_ssh.py b/fence/agents/ilo_ssh/fence_ilo_ssh.py index fb2d9f3f..b0366157 100644 --- a/fence/agents/ilo_ssh/fence_ilo_ssh.py +++ b/fence/agents/ilo_ssh/fence_ilo_ssh.py @@ -1,74 +1,68 @@ #!@PYTHON@ -tt import sys, re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(conn, options): conn.send_eol("show /system1") re_state = re.compile('EnabledState=(.*)', re.IGNORECASE) conn.log_expect(re_state, int(options["--shell-timeout"])) status = conn.match.group(1).lower() if status.startswith("enabled"): return "on" else: return "off" def set_power_status(conn, options): if options["--action"] == "on": conn.send_eol("start /system1") else: conn.send_eol("power off hard") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) return def reboot_cycle(conn, options): conn.send_eol("reset hard /system1") conn.log_expect(options["--command-prompt"], int(options["--power-timeout"])) return def main(): device_opt = ["ipaddr", "login", "passwd", "secure", "cmd_prompt", "method", "telnet"] atexit.register(atexit_handler) all_opt["cmd_prompt"]["default"] = ["MP>", "hpiLO->"] all_opt["power_wait"]["default"] = 5 options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for HP iLO over SSH" docs["longdesc"] = "fence_ilo_ssh is a fence agent that connects to iLO device. It logs into \ device via ssh and reboot a specified outlet. " docs["vendorurl"] = "http://www.hp.com" docs["symlink"] = [("fence_ilo3_ssh", "Fence agent for HP iLO3 over SSH"), ("fence_ilo4_ssh", "Fence agent for HP iLO4 over SSH")] show_docs(options, docs) options["eol"] = "\r" conn = fence_login(options) conn.send_eol("SMCLP") ## ## Fence operations #### result = fence_action(conn, options, set_power_status, get_power_status, None, reboot_cycle) fence_logout(conn, "exit") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/intelmodular/fence_intelmodular.py b/fence/agents/intelmodular/fence_intelmodular.py index 4e1301c9..294ea4dd 100644 --- a/fence/agents/intelmodular/fence_intelmodular.py +++ b/fence/agents/intelmodular/fence_intelmodular.py @@ -1,92 +1,86 @@ #!@PYTHON@ -tt # Tested with an Intel MFSYS25 using firmware package 2.6 Should work with an # MFSYS35 as well. # # Notes: # # The manual and firmware release notes says SNMP is read only. This is not # true, as per the MIBs that ship with the firmware you can write to # the bladePowerLed oid to control the servers. # # Thanks Matthew Kent for original agent and testing. import sys import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Intel Modular SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # From INTELCORPORATION-MULTI-FLEX-SERVER-BLADES-MIB.my that ships with # firmware updates STATUSES_OID = ".1.3.6.1.4.1.343.2.19.1.2.10.202.1.1.6" # Status constants returned as value from SNMP STATUS_UP = 2 STATUS_DOWN = 0 # Status constants to set as value to SNMP STATUS_SET_ON = 2 STATUS_SET_OFF = 3 ### FUNCTIONS ### def get_power_status(conn, options): (_, status) = conn.get("%s.%s"% (STATUSES_OID, options["--plug"])) return status == str(STATUS_UP) and "on" or "off" def set_power_status(conn, options): conn.set("%s.%s" % (STATUSES_OID, options["--plug"]), (options["--action"] == "on" and STATUS_SET_ON or STATUS_SET_OFF)) def get_outlets_status(conn, options): result = {} res_blades = conn.walk(STATUSES_OID, 30) for x in res_blades: port_num = x[0].split('.')[-1] port_alias = "" port_status = (x[1] == str(STATUS_UP) and "on" or "off") result[port_num] = (port_alias, port_status) return result # Main agent method def main(): device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", "port", "snmp_version", "snmp"] atexit.register(atexit_handler) options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Intel Modular" docs["longdesc"] = "fence_intelmodular is an I/O Fencing agent \ which can be used with Intel Modular device (tested on Intel MFSYS25, should \ work with MFSYS35 as well). \ \n.P\n\ Note: Since firmware update version 2.7, SNMP v2 write support is \ removed, and replaced by SNMP v3 support. So agent now has default \ SNMP version 3. If you are using older firmware, please supply -d \ for command line and snmp_version option for your cluster.conf." docs["vendorurl"] = "http://www.intel.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ipdu/fence_ipdu.py b/fence/agents/ipdu/fence_ipdu.py index 7e648598..da34d2b6 100644 --- a/fence/agents/ipdu/fence_ipdu.py +++ b/fence/agents/ipdu/fence_ipdu.py @@ -1,159 +1,153 @@ #!@PYTHON@ -tt # The Following agent has been tested on: # IBM iPDU model 46M4002 # Firmware release OPDP_sIBM_v01.2_1 # import sys import atexit import logging sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage from fencing_snmp import FencingSnmp -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="IBM iPDU SNMP fence agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - ### CONSTANTS ### # oid defining fence device OID_SYS_OBJECT_ID = '.1.3.6.1.2.1.1.2.0' ### GLOBAL VARIABLES ### # Device - see IBM iPDU device = None # Port ID port_id = None # Switch ID switch_id = None # Classes describing Device params class IBMiPDU(object): # iPDU status_oid = '.1.3.6.1.4.1.2.6.223.8.2.2.1.11.%d' control_oid = '.1.3.6.1.4.1.2.6.223.8.2.2.1.11.%d' outlet_table_oid = '.1.3.6.1.4.1.2.6.223.8.2.2.1.2' ident_str = "IBM iPDU" state_on = 1 state_off = 0 turn_on = 1 turn_off = 0 has_switches = False ### FUNCTIONS ### def ipdu_set_device(conn, options): global device agents_dir = {'.1.3.6.1.4.1.2.6.223':IBMiPDU, None:IBMiPDU} # First resolve type of PDU device pdu_type = conn.walk(OID_SYS_OBJECT_ID) if not ((len(pdu_type) == 1) and (pdu_type[0][1] in agents_dir)): pdu_type = [[None, None]] device = agents_dir[pdu_type[0][1]] logging.debug("Trying %s"%(device.ident_str)) def ipdu_resolv_port_id(conn, options): global port_id, switch_id if device == None: ipdu_set_device(conn, options) # Now we resolv port_id/switch_id if options["--plug"].isdigit() and ((not device.has_switches) or (options["--switch"].isdigit())): port_id = int(options["--plug"]) if device.has_switches: switch_id = int(options["--switch"]) else: table = conn.walk(device.outlet_table_oid, 30) for x in table: if x[1].strip('"') == options["--plug"]: t = x[0].split('.') if device.has_switches: port_id = int(t[len(t)-1]) switch_id = int(t[len(t)-3]) else: port_id = int(t[len(t)-1]) if port_id == None: fail_usage("Can't find port with name %s!"%(options["--plug"])) def get_power_status(conn, options): if port_id == None: ipdu_resolv_port_id(conn, options) oid = ((device.has_switches) and device.status_oid%(switch_id, port_id) or device.status_oid%(port_id)) (oid, status) = conn.get(oid) return status == str(device.state_on) and "on" or "off" def set_power_status(conn, options): if port_id == None: ipdu_resolv_port_id(conn, options) oid = ((device.has_switches) and device.control_oid%(switch_id, port_id) or device.control_oid%(port_id)) conn.set(oid, (options["--action"] == "on" and device.turn_on or device.turn_off)) def get_outlets_status(conn, options): result = {} if device == None: ipdu_set_device(conn, options) res_ports = conn.walk(device.outlet_table_oid, 30) for x in res_ports: t = x[0].split('.') port_num = ((device.has_switches) and "%s:%s"%(t[len(t)-3], t[len(t)-1]) or "%s"%(t[len(t)-1])) port_name = x[1].strip('"') port_status = "" result[port_num] = (port_name, port_status) return result # Main agent method def main(): global device device_opt = ["ipaddr", "login", "passwd", "no_login", "no_password", \ "port", "snmp_version", "snmp"] atexit.register(atexit_handler) all_opt["snmp_version"]["default"] = "3" all_opt["community"]["default"] = "private" all_opt["switch"]["default"] = "1" device = IBMiPDU options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for iPDU over SNMP" docs["longdesc"] = "fence_ipdu is an I/O Fencing agent \ which can be used with the IBM iPDU network power switch. It logs \ into a device via SNMP and reboots a specified outlet. It supports \ SNMP v3 with all combinations of authenticity/privacy settings." docs["vendorurl"] = "http://www.ibm.com" show_docs(options, docs) # Operate the fencing device result = fence_action(FencingSnmp(options), options, set_power_status, get_power_status, get_outlets_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ipmilan/fence_ipmilan.py b/fence/agents/ipmilan/fence_ipmilan.py index 6c43b185..13a14785 100644 --- a/fence/agents/ipmilan/fence_ipmilan.py +++ b/fence/agents/ipmilan/fence_ipmilan.py @@ -1,219 +1,213 @@ #!@PYTHON@ -tt import sys, re, os import atexit from pipes import quote sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, is_executable, run_command, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(_, options): output = _run_command(options, "status") match = re.search('[Cc]hassis [Pp]ower is [\\s]*([a-zA-Z]{2,3})', str(output)) status = match.group(1) if match else None return status def set_power_status(_, options): _run_command(options, options["--action"]) return def reboot_cycle(_, options): output = _run_command(options, "cycle") return bool(re.search('chassis power control: cycle', str(output).lower())) def reboot_diag(_, options): output = _run_command(options, "diag") return bool(re.search('chassis power control: diag', str(output).lower())) def _run_command(options, action): cmd, log_cmd = create_command(options, action) return run_command(options, cmd, log_command=log_cmd) def create_command(options, action): class Cmd: cmd = "" log = "" @classmethod def append(cls, cmd, log=None): cls.cmd += cmd cls.log += (cmd if log is None else log) # --use-sudo / -d if "--use-sudo" in options: Cmd.append(options["--sudo-path"] + " ") Cmd.append(options["--ipmitool-path"]) # --lanplus / -L if "--lanplus" in options and options["--lanplus"] in ["", "1"]: Cmd.append(" -I lanplus") else: Cmd.append(" -I lan") # --ip / -a Cmd.append(" -H " + options["--ip"]) # --port / -n if "--ipport" in options: Cmd.append(" -p " + options["--ipport"]) # --target if "--target" in options: Cmd.append(" -t " + options["--target"]) # --username / -l if "--username" in options and len(options["--username"]) != 0: Cmd.append(" -U " + quote(options["--username"])) # --auth / -A if "--auth" in options: Cmd.append(" -A " + options["--auth"]) # --password / -p if "--password" in options: Cmd.append(" -P " + quote(options["--password"]), " -P [set]") else: Cmd.append(" -P ''", " -P [set]") # --cipher / -C if "--cipher" in options: Cmd.append(" -C " + options["--cipher"]) if "--privlvl" in options: Cmd.append(" -L " + options["--privlvl"]) if "--hexadecimal-kg" in options: Cmd.append(" -y " + options["--hexadecimal-kg"]) # --action / -o Cmd.append(" chassis power " + action) return (Cmd.cmd, Cmd.log) def define_new_opts(): all_opt["lanplus"] = { "getopt" : "P", "longopt" : "lanplus", "help" : "-P, --lanplus Use Lanplus to improve security of connection", "required" : "0", "default" : "0", "shortdesc" : "Use Lanplus to improve security of connection", "order": 1 } all_opt["auth"] = { "getopt" : "A:", "longopt" : "auth", "help" : "-A, --auth=[auth] IPMI Lan Auth type (md5|password|none)", "required" : "0", "shortdesc" : "IPMI Lan Auth type.", "choices" : ["md5", "password", "none"], "order": 1 } all_opt["cipher"] = { "getopt" : "C:", "longopt" : "cipher", "help" : "-C, --cipher=[cipher] Ciphersuite to use (same as ipmitool -C parameter)", "required" : "0", "shortdesc" : "Ciphersuite to use (same as ipmitool -C parameter)", "order": 1 } all_opt["privlvl"] = { "getopt" : "L:", "longopt" : "privlvl", "help" : "-L, --privlvl=[level] " "Privilege level on IPMI device (callback|user|operator|administrator)", "required" : "0", "shortdesc" : "Privilege level on IPMI device", "default" : "administrator", "choices" : ["callback", "user", "operator", "administrator"], "order": 1 } all_opt["ipmitool_path"] = { "getopt" : ":", "longopt" : "ipmitool-path", "help" : "--ipmitool-path=[path] Path to ipmitool binary", "required" : "0", "shortdesc" : "Path to ipmitool binary", "default" : "@IPMITOOL_PATH@", "order": 200 } all_opt["target"] = { "getopt" : ":", "longopt" : "target", "help" : "--target=[targetaddress] Bridge IPMI requests to the remote target address", "required" : "0", "shortdesc" : "Bridge IPMI requests to the remote target address", "order": 1 } all_opt["hexadecimal_kg"] = { "getopt" : ":", "longopt" : "hexadecimal-kg", "help" : "--hexadecimal-kg=[key] Hexadecimal-encoded Kg key for IPMIv2 authentication", "required" : "0", "shortdesc" : "Hexadecimal-encoded Kg key for IPMIv2 authentication", "order": 1 } def main(): atexit.register(atexit_handler) device_opt = ["ipaddr", "login", "no_login", "no_password", "passwd", "diag", "lanplus", "auth", "cipher", "privlvl", "sudo", "ipmitool_path", "method", "target", "hexadecimal_kg"] define_new_opts() all_opt["power_wait"]["default"] = 2 if os.path.basename(sys.argv[0]) == "fence_ilo3": all_opt["power_wait"]["default"] = "4" all_opt["method"]["default"] = "cycle" all_opt["lanplus"]["default"] = "1" elif os.path.basename(sys.argv[0]) == "fence_ilo4": all_opt["lanplus"]["default"] = "1" all_opt["ipport"]["default"] = "623" if all_opt["method"]["default"] == "cycle": all_opt["method"]["help"] = "-m, --method=[method] Method to fence (onoff|cycle) (Default: cycle)\n" \ "WARNING! This fence agent might report success before the node is powered off. " \ "You should use -m/method onoff if your fence device works correctly with that option." options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for IPMI" docs["longdesc"] = "fence_ipmilan is an I/O Fencing agent\ which can be used with machines controlled by IPMI.\ This agent calls support software ipmitool (http://ipmitool.sf.net/). \ WARNING! This fence agent might report success before the node is powered off. \ You should use -m/method onoff if your fence device works correctly with that option." docs["vendorurl"] = "" docs["symlink"] = [("fence_ilo3", "Fence agent for HP iLO3"), ("fence_ilo4", "Fence agent for HP iLO4"), ("fence_imm", "Fence agent for IBM Integrated Management Module"), ("fence_idrac", "Fence agent for Dell iDRAC")] show_docs(options, docs) run_delay(options) if not is_executable(options["--ipmitool-path"]): fail_usage("Ipmitool not found or not accessible") reboot_fn = reboot_cycle if options["--action"] == "diag": # Diag is a special action that can't be verified so we will reuse reboot functionality # to minimize impact on generic library options["--action"] = "reboot" options["--method"] = "cycle" reboot_fn = reboot_diag result = fence_action(None, options, set_power_status, get_power_status, None, reboot_fn) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ironic/fence_ironic.py b/fence/agents/ironic/fence_ironic.py index 8e5d5b0f..66d84fca 100644 --- a/fence/agents/ironic/fence_ironic.py +++ b/fence/agents/ironic/fence_ironic.py @@ -1,136 +1,130 @@ #!@PYTHON@ -tt import atexit import logging import os import re import sys from pipes import quote sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage, is_executable, run_command, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_name_or_uuid(options): return options["--uuid"] if "--uuid" in options else options["--plug"] def get_power_status(_, options): output = ironic_run_command(options, "status") stdout = output[1] match = re.search('power[\\s]*([a-zA-Z]{2,3})', str(stdout)) status = match.group(1) if match else None return status def set_power_status(_, options): ironic_run_command(options, options["--action"]) return def get_devices_list(_, options): nodes = {} output = ironic_run_command(options, "list") stdout = output[1] for line in stdout.splitlines(): uuid = "UUID" try: (uuid, name, state) = line.split(',') except ValueError: pass if "UUID" in uuid: continue # skip line header match = re.search('power[\\s]*([a-zA-Z]{2,3})', state) status = match.group(1) if match else None nodes[uuid] = (name, status) return nodes def ironic_run_command(options, action, timeout=None): cmd = options["--openstack-path"] + " baremetal" env = os.environ.copy() # --username / -l if "--username" in options and len(options["--username"]) != 0: env["OS_USERNAME"] = options["--username"] # --password / -p if "--password" in options: env["OS_PASSWORD"] = options["--password"] # --tenant-name -t if "--tenant-name" in options: env["OS_TENANT_NAME"] = options["--tenant-name"] # --auth-url if "--auth-url" in options: env["OS_AUTH_URL"] = options["--auth-url"] # --action / -o if action == "status": cmd += " show %s -c power_state --format value" % (get_name_or_uuid(options)) elif action in ["on", "off"]: cmd += " power %s %s" % (action, get_name_or_uuid(options)) elif action == "list": cmd += " list -c 'Instance UUID' -c Name -c 'Power State' --format csv --quote minimal" logging.debug("cmd -> %s" % cmd) return run_command(options, cmd, timeout, env) def define_new_opts(): all_opt["auth-url"] = { "getopt" : ":", "longopt" : "auth-url", "help" : "--auth-url=[authurl] Auth URL", "required" : "1", "shortdesc" : "Keystone Admin Auth URL", "order": 1 } all_opt["tenant-name"] = { "getopt" : "t:", "longopt" : "tenant-name", "help" : "-t, --tenant-name=[tenant] Tenantname", "required" : "0", "shortdesc" : "Keystone Admin Tenant", "default": "admin", "order": 1 } all_opt["openstack-path"] = { "getopt" : ":", "longopt" : "openstack-path", "help" : "--openstack-path=[path] Path to openstack binary", "required" : "0", "shortdesc" : "Path to the OpenStack binary", "default" : "@OPENSTACK_PATH@", "order": 200 } def main(): atexit.register(atexit_handler) device_opt = ["login", "passwd", "port", "auth-url", "tenant-name", "openstack-path"] define_new_opts() options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for OpenStack's Ironic (Bare Metal as a service) service" docs["longdesc"] = "fence_ironic is a Fencing agent \ which can be used with machines controlled by the Ironic service. \ This agent calls the openstack CLI. \ WARNING! This fence agent is not intended for production use. Relying on a functional ironic service for fencing is not a good design choice." docs["vendorurl"] = "https://wiki.openstack.org/wiki/Ironic" show_docs(options, docs) run_delay(options) if not is_executable(options["--openstack-path"]): fail_usage("openstack tool not found or not accessible") result = fence_action(None, options, set_power_status, get_power_status, get_devices_list) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/ldom/fence_ldom.py b/fence/agents/ldom/fence_ldom.py index b4d87249..0cb3320b 100644 --- a/fence/agents/ldom/fence_ldom.py +++ b/fence/agents/ldom/fence_ldom.py @@ -1,108 +1,102 @@ #!@PYTHON@ -tt ## ## The Following Agent Has Been Tested On - LDOM 1.0.3 ## The interface is backward compatible so it will work ## with 1.0, 1.0.1 and .2 too. ## ##### import sys, re, pexpect import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail_usage -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Logical Domains (LDoms) fence Agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - COMMAND_PROMPT_REG = r"\[PEXPECT\]$" COMMAND_PROMPT_NEW = "[PEXPECT]" # Start comunicating after login. Prepare good environment. def start_communication(conn, options): conn.send_eol("PS1='" + COMMAND_PROMPT_NEW + "'") res = conn.expect([pexpect.TIMEOUT, COMMAND_PROMPT_REG], int(options["--shell-timeout"])) if res == 0: #CSH stuff conn.send_eol("set prompt='" + COMMAND_PROMPT_NEW + "'") conn.log_expect(COMMAND_PROMPT_REG, int(options["--shell-timeout"])) def get_power_status(conn, options): start_communication(conn, options) conn.send_eol("ldm ls") conn.log_expect(COMMAND_PROMPT_REG, int(options["--shell-timeout"])) result = {} #This is status of mini finite automata. 0 = we didn't found NAME and STATE, 1 = we did fa_status = 0 for line in conn.before.splitlines(): domain = re.search(r"^(\S+)\s+(\S+)\s+.*$", line) if domain != None: if fa_status == 0 and domain.group(1) == "NAME" and domain.group(2) == "STATE": fa_status = 1 elif fa_status == 1: result[domain.group(1)] = ("", (domain.group(2).lower() == "bound" and "off" or "on")) if not options["--action"] in ['monitor', 'list']: if not options["--plug"] in result: fail_usage("Failed: You have to enter existing logical domain!") else: return result[options["--plug"]][1] else: return result def set_power_status(conn, options): start_communication(conn, options) cmd_line = "ldm "+ (options["--action"] == "on" and "start" or "stop -f") + " \"" + options["--plug"] + "\"" conn.send_eol(cmd_line) conn.log_expect(COMMAND_PROMPT_REG, int(options["--power-timeout"])) def main(): device_opt = ["ipaddr", "login", "passwd", "cmd_prompt", "secure", "port"] atexit.register(atexit_handler) all_opt["secure"]["default"] = "1" all_opt["cmd_prompt"]["default"] = [r"\ $"] options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for Sun LDOM" docs["longdesc"] = "fence_ldom is an I/O Fencing agent \ which can be used with LDoms virtual machines. This agent works \ so, that run ldm command on host machine. So ldm must be directly \ runnable.\ \n.P\n\ Very useful parameter is -c (or cmd_prompt in stdin mode). This \ must be set to something, what is displayed after successful login \ to host machine. Default string is space on end of string (default \ for root in bash). But (for example) csh use ], so in that case you \ must use parameter -c with argument ]. Very similar situation is, \ if you use bash and login to host machine with other user than \ root. Than prompt is $, so again, you must use parameter -c." docs["vendorurl"] = "http://www.sun.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, "logout") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/lib/fencing.py.py b/fence/agents/lib/fencing.py.py index 4001787d..f47e4133 100644 --- a/fence/agents/lib/fencing.py.py +++ b/fence/agents/lib/fencing.py.py @@ -1,1451 +1,1445 @@ #!@PYTHON@ -tt import sys, getopt, time, os, uuid, pycurl, stat import pexpect, re, syslog import logging import subprocess import threading import shlex import socket import textwrap import __main__ -## do not add code here. -#BEGIN_VERSION_GENERATION -RELEASE_VERSION = "New fence lib agent - test release on steroids" -REDHAT_COPYRIGHT = "" -BUILD_DATE = "March, 2008" -#END_VERSION_GENERATION +RELEASE_VERSION = "@RELEASE_VERSION@" __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", "type" : "second", "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", "type" : "integer", "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" : 1}, "diag" : { "getopt" : "", "help" : "", "order" : 1}, "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", "type" : "second", "help" : "--login-timeout=[seconds] Wait X seconds for cmd prompt after login", "default" : "5", "required" : "0", "order" : 200}, "shell_timeout" : { "getopt" : ":", "longopt" : "shell-timeout", "type" : "second", "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", "type" : "second", "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", "type" : "second", "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", "type" : "integer", "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" : "@TELNET_PATH@", "order": 300}, "ssh_path" : { "getopt" : ":", "longopt" : "ssh-path", "help" : "--ssh-path=[path] Path to ssh binary", "required" : "0", "default" : "@SSH_PATH@", "order": 300}, "gnutlscli_path" : { "getopt" : ":", "longopt" : "gnutlscli-path", "help" : "--gnutlscli-path=[path] Path to gnutls-cli binary", "required" : "0", "default" : "@GNUTLSCLI_PATH@", "order": 300}, "sudo_path" : { "getopt" : ":", "longopt" : "sudo-path", "help" : "--sudo-path=[path] Path to sudo binary", "required" : "0", "default" : "@SUDO_PATH@", "order": 300}, "snmpwalk_path" : { "getopt" : ":", "longopt" : "snmpwalk-path", "help" : "--snmpwalk-path=[path] Path to snmpwalk binary", "required" : "0", "default" : "@SNMPWALK_PATH@", "order" : 300}, "snmpset_path" : { "getopt" : ":", "longopt" : "snmpset-path", "help" : "--snmpset-path=[path] Path to snmpset binary", "required" : "0", "default" : "@SNMPSET_PATH@", "order" : 300}, "snmpget_path" : { "getopt" : ":", "longopt" : "snmpget-path", "help" : "--snmpget-path=[path] Path to snmpget binary", "required" : "0", "default" : "@SNMPGET_PATH@", "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 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", self.before + 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 opt in DEPENDENCY_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(key=lambda x: x[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)) if "longopt" in all_opt[key]] # Find keys that are going to replace inconsistent names mapping = dict([(opt["longopt"].replace("-", "_"), key) for (key, opt) in sorted_list if (key != opt["longopt"].replace("-", "_"))]) new_options = [(key, all_opt[mapping[key]]) for key in mapping] sorted_list.extend(new_options) sorted_list.sort(key=lambda x: (x[1]["order"], x[0])) print("") print("") for (symlink, desc) in docs.get("symlink", []): print("") print("" + docs["longdesc"] + "") print("" + docs["vendorurl"] + "") print("") for (key, opt) in sorted_list: info = "" if key in all_opt: if key != all_opt[key].get('longopt', key).replace("-", "_"): info = "deprecated=\"1\"" else: info = "obsoletes=\"%s\"" % (mapping.get(key)) if "help" in opt and len(opt["help"]) > 0: if info != "": info = " " + info print("\t") default = "" if "default" in opt: default = "default=\"" + _encode_html_entities(str(opt["default"])) + "\" " mixed = opt["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 opt: shortdesc = re.sub("\s\s+", " ", opt["help"][31:]) else: shortdesc = opt["shortdesc"] print("\t\t") if "choices" in opt: print("\t\t") for choice in opt["choices"]: print("\t\t\t") elif opt["getopt"].count(":") > 0: t = opt.get("type", "string") print("\t\t") else: print("\t\t") print("\t\t" + shortdesc + "") print("\t") print("") print("") (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" % (on_target, avail_opt.count("fabric_fencing"))) for action in available_actions: print("\t" % (action)) print("") print("") 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(k in options for k in ("--help", "--version")): return options if "--verbose" in options: logging.getLogger().setLevel(logging.DEBUG) ## add logging to syslog logging.getLogger().addHandler(SyslogLibHandler()) if "--quiet" not in options: ## 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: if not _validate_input(options, False): fail_usage("validate-all failed") sys.exit(EC_OK) else: _validate_input(options, True) if "--debug-file" in options: 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 "--snmp-priv-passwd-script" in options: options["--snmp-priv-passwd"] = os.popen(options["--snmp-priv-passwd-script"]).read().rstrip() if "--password-script" in options: 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 "--plugs" in options 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 "--plugs" in options 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 range(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 "--help" in options: 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 "--version" in options: - print(__main__.RELEASE_VERSION, __main__.BUILD_DATE) - print(__main__.REDHAT_COPYRIGHT) + print(RELEASE_VERSION) 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 "--plug" in options: 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 list(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 as 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 as ex: logging.error("%s\n", str(ex)) fail(EC_TIMED_OUT) except socket.timeout as 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 "eol" not in options: options["eol"] = "\r\n" if "--command-prompt" in options and type(options["--command-prompt"]) is not list: options["--command-prompt"] = [options["--command-prompt"]] try: if "--ssl" in options: conn = _open_ssl_connection(options) elif "--ssh" in options and "--identity-file" not in options: conn = _login_ssh_with_password(options, re_login_string) elif "--ssh" in options and "--identity-file" in options: conn = _login_ssh_with_identity_file(options) else: conn = _login_telnet(options, re_login_string) except pexpect.EOF as exception: logging.debug("%s", str(exception)) fail(EC_LOGIN_DENIED) except pexpect.TIMEOUT as 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 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 "--notls" in options: gnutls_opts = "--priority \"NORMAL:-VERS-TLS1.2:-VERS-TLS1.1:-VERS-TLS1.0:+VERS-SSL3.0\"" elif "--tls1.0" in options: 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 "--ssl-insecure" in options: 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 as ex: logging.error("%s\n", str(ex)) sys.exit(EC_GENERIC_ERROR) return conn def _login_ssh_with_identity_file(options): if "--inet6-only" in options: force_ipvx = "-6 " elif "--inet4-only" in options: 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 "--ssh-options" in 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 "--password" in options: 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 "--inet6-only" in options: force_ipvx = "-6 " elif "--inet4-only" in options: 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 "--ssh-options" in options: command += ' ' + options["--ssh-options"] conn = fspawn(options, command) if "telnet_over_ssh" in options: # 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 "default" in all_opt["ipport"]: 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 "default" in all_opt[opt] and not opt == "ipport": getopt_long = "--" + all_opt[opt]["longopt"] if getopt_long not in options: 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 "--username" not in options 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 "--ip" not in options and "--managed" not in options and "--target" not in options: 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 ("--password" in options or "--password-script" in options): valid_input = False fail_usage("Failed: You have to enter password or password script", stop) else: if not ("--password" in options or \ "--password-script" in options or "--identity-file" in options): valid_input = False fail_usage("Failed: You have to enter password, password script or identity file", stop) if "--ssh" not in options and "--identity-file" in options: valid_input = False fail_usage("Failed: You have to use identity file together with ssh connection (-x)", stop) if "--identity-file" in options 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 \ "--plug" not in options 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 "--plug" in options and len(options["--plug"].split(",")) > 1 and \ "--method" in options 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) for failed_opt in _get_opts_with_invalid_types(options): valid_input = False if all_opt[failed_opt]["type"] == "second": fail_usage("Failed: The value you have entered for %s is not a valid time in seconds" % \ ("--" + all_opt[failed_opt]["longopt"]), stop) else: fail_usage("Failed: The value you have entered for %s is not a valid %s" % \ ("--" + all_opt[failed_opt]["longopt"], all_opt[failed_opt]["type"]), stop) return valid_input def _encode_html_entities(text): return text.replace("&", "&").replace('"', """).replace('<', "<"). \ replace('>', ">").replace("'", "'") def _prepare_getopt_args(options): getopt_string = "" longopt_list = [] for k in options: if k in all_opt and all_opt[k]["getopt"] != ":": # getopt == ":" means that opt is without short getopt, but has value getopt_string += all_opt[k]["getopt"] elif k not in all_opt: fail_usage("Parse error: unknown option '"+k+"'") if k in all_opt and "longopt" in all_opt[k]: 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 = "" mapping_longopt_names = dict([(all_opt[o].get("longopt"), o) for o in avail_opt]) for line in sys.stdin.readlines(): line = line.strip() if (line.startswith("#")) or (len(line) == 0): continue (name, value) = (line + "=").split("=", 1) name = name.replace("-", "_"); value = value[:-1] if name in mapping_longopt_names: name = mapping_longopt_names[name] 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 as 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 list(dict(entered_opt).keys()): all_key = [key for (key, value) in list(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 "choices" in all_opt[opt]: longopt = "--" + all_opt[opt]["longopt"] possible_values_upper = [y.upper() for y in all_opt[opt]["choices"]] if longopt in options: options[longopt] = options[longopt].upper() if not options["--" + all_opt[opt]["longopt"]] in possible_values_upper: options_failed.append(opt) return options_failed def _get_opts_with_invalid_types(options): options_failed = [] device_opt = options["device_opt"] for opt in device_opt: if "type" in all_opt[opt]: longopt = "--" + all_opt[opt]["longopt"] if longopt in options: if all_opt[opt]["type"] in ["integer", "second"]: try: number = int(options["--" + all_opt[opt]["longopt"]]) except ValueError: 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") if device_opt.count("diag"): available_actions.append("diag") return (available_actions, default_value) diff --git a/fence/agents/lib/fencing_snmp.py.py b/fence/agents/lib/fencing_snmp.py.py index 9aaf52be..7a59b88c 100644 --- a/fence/agents/lib/fencing_snmp.py.py +++ b/fence/agents/lib/fencing_snmp.py.py @@ -1,134 +1,128 @@ #!@PYTHON@ -tt # For example of use please see fence_cisco_mds import re, pexpect import logging from fencing import * from fencing import fail, fail_usage, EC_TIMED_OUT, run_delay __all__ = ['FencingSnmp'] ## do not add code here. -#BEGIN_VERSION_GENERATION -RELEASE_VERSION = "" -REDHAT_COPYRIGHT = "" -BUILD_DATE = "" -#END_VERSION_GENERATION - class FencingSnmp: def __init__(self, options): self.options = options run_delay(options) def quote_for_run(self, string): return string.replace(r"'", "'\\''") def complete_missed_params(self): mapping = [[ ['snmp-priv-passwd', 'password', '!snmp-sec-level'], 'self.options["--snmp-sec-level"]="authPriv"' ], [ ['!snmp-version', 'community', '!username', '!snmp-priv-passwd', '!password'], 'self.options["--snmp-version"]="2c"' ]] for val in mapping: e = val[0] res = True for item in e: if item[0] == '!' and "--" + item[1:] in self.options: res = False break if item[0] != '!' and "--" + item[0:] not in self.options: res = False break if res: exec(val[1]) def prepare_cmd(self, command): cmd = "%s -m '' -Oeqn "% (command) self.complete_missed_params() #mapping from our option to snmpcmd option mapping = (('snmp-version', 'v'), ('community', 'c')) for item in mapping: if "--" + item[0] in self.options: cmd += " -%s '%s'"% (item[1], self.quote_for_run(self.options["--" + item[0]])) # Some options make sense only for v3 (and for v1/2c can cause "problems") if ("--snmp-version" in self.options) and (self.options["--snmp-version"] == "3"): # Mapping from our options to snmpcmd options for v3 mapping_v3 = (('snmp-auth-prot', 'a'), ('snmp-sec-level', 'l'), ('snmp-priv-prot', 'x'), \ ('snmp-priv-passwd', 'X'), ('password', 'A'), ('username', 'u')) for item in mapping_v3: if "--"+item[0] in self.options: cmd += " -%s '%s'"% (item[1], self.quote_for_run(self.options["--" + item[0]])) force_ipvx = "" if "--inet6-only" in self.options: force_ipvx = "udp6:" if "--inet4-only" in self.options: force_ipvx = "udp:" cmd += " '%s%s%s'"% (force_ipvx, self.quote_for_run(self.options["--ip"]), "--ipport" in self.options and self.quote_for_run(":" + str(self.options["--ipport"])) or "") return cmd def run_command(self, command, additional_timemout=0): try: logging.debug("%s\n", command) (res_output, res_code) = pexpect.run(command, int(self.options["--shell-timeout"]) + int(self.options["--login-timeout"]) + additional_timemout, True) if res_code == None: fail(EC_TIMED_OUT) logging.debug("%s\n", res_output) if (res_code != 0) or (re.search("^Error ", res_output, re.MULTILINE) != None): fail_usage("Returned %d: %s"% (res_code, res_output)) except pexpect.ExceptionPexpect: fail_usage("Cannot run command %s"%(command)) return res_output def get(self, oid, additional_timemout=0): cmd = "%s '%s'"% (self.prepare_cmd(self.options["--snmpget-path"]), self.quote_for_run(oid)) output = self.run_command(cmd, additional_timemout).splitlines() return output[len(output)-1].split(None, 1) def set(self, oid, value, additional_timemout=0): mapping = ((int, 'i'), (str, 's')) type_of_value = '' for item in mapping: if isinstance(value, item[0]): type_of_value = item[1] break cmd = "%s '%s' %s '%s'" % (self.prepare_cmd(self.options["--snmpset-path"]), self.quote_for_run(oid), type_of_value, self.quote_for_run(str(value))) self.run_command(cmd, additional_timemout) def walk(self, oid, additional_timemout=0): cmd = "%s '%s'"% (self.prepare_cmd(self.options["--snmpwalk-path"]), self.quote_for_run(oid)) output = self.run_command(cmd, additional_timemout).splitlines() return [x.split(None, 1) for x in output if x.startswith(".")] diff --git a/fence/agents/lpar/fence_lpar.py b/fence/agents/lpar/fence_lpar.py index 5487b84c..270bbe3b 100644 --- a/fence/agents/lpar/fence_lpar.py +++ b/fence/agents/lpar/fence_lpar.py @@ -1,181 +1,175 @@ #!@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"] in ["4", "IVM"]: 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"] in ["4", "IVM"]: 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: try: (port, status) = outlet_line.split(":") except ValueError: fail_usage('Output does not match expected HMC version, try different one'); outlets[port] = ("", status) elif options["--hmc-version"] == "IVM": 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: try: (port, status) = outlet_line.split(",") except ValueError: fail_usage('Output does not match expected HMC version, try different one'); 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|ivm) (default: 4)", "required" : "0", "shortdesc" : "Force HMC version to use", "default" : "4", "choices" : ["3", "4", "ivm"], "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 "--managed" not in options: 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/fence/agents/mpath/fence_mpath.py b/fence/agents/mpath/fence_mpath.py index 5426a18d..183c38a0 100644 --- a/fence/agents/mpath/fence_mpath.py +++ b/fence/agents/mpath/fence_mpath.py @@ -1,251 +1,245 @@ #!@PYTHON@ -tt import sys import stat import re import os import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import fail_usage, run_command, atexit_handler, check_input, process_input, show_docs from fencing import fence_action, all_opt, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_status(conn, options): del conn status = "off" for dev in options["devices"]: is_block_device(dev) if options["--key"] in get_registration_keys(options, dev): status = "on" else: logging.debug("No registration for key "\ + options["--key"] + " on device " + dev + "\n") if options["--action"] == "monitor": dev_read(options) return status def set_status(conn, options): del conn count = 0 if options["--action"] == "on": for dev in options["devices"]: is_block_device(dev) register_dev(options, dev) if options["--key"] not in get_registration_keys(options, dev): count += 1 logging.debug("Failed to register key "\ + options["--key"] + "on device " + dev + "\n") continue dev_write(options, dev) if get_reservation_key(options, dev) is None \ and not reserve_dev(options, dev) \ and get_reservation_key(options, dev) is None: count += 1 logging.debug("Failed to create reservation (key="\ + options["--key"] + ", device=" + dev + ")\n") else: dev_keys = dev_read(options) for dev in options["devices"]: is_block_device(dev) if options["--key"] in get_registration_keys(options, dev): preempt_abort(options, dev_keys[dev], dev) for dev in options["devices"]: if options["--key"] in get_registration_keys(options, dev): count += 1 logging.debug("Failed to remove key "\ + options["--key"] + " on device " + dev + "\n") continue if not get_reservation_key(options, dev): count += 1 logging.debug("No reservation exists on device " + dev + "\n") if count: logging.error("Failed to verify " + str(count) + " device(s)") sys.exit(1) #run command, returns dict, ret["err"] = exit code; ret["out"] = output def run_cmd(options, cmd): ret = {} if "--use-sudo" in options: prefix = options["--sudo-path"] + " " else: prefix = "" (ret["err"], ret["out"], _) = run_command(options, prefix + cmd) ret["out"] = "".join([i for i in ret["out"] if i is not None]) return ret # check if device exist and is block device def is_block_device(dev): if not os.path.exists(dev): fail_usage("Failed: device \"" + dev + "\" does not exist") if not stat.S_ISBLK(os.stat(dev).st_mode): fail_usage("Failed: device \"" + dev + "\" is not a block device") # cancel registration def preempt_abort(options, host, dev): cmd = options["--mpathpersist-path"] + " -o --preempt-abort --prout-type=5 --param-rk=" + host +" --param-sark=" + options["--key"] +"-d " + dev return not bool(run_cmd(options, cmd)["err"]) def register_dev(options, dev): cmd = options["--mpathpersist-path"] + " -o --register --param-sark=" + options["--key"] + " -d " + dev #cmd return code != 0 but registration can be successful return not bool(run_cmd(options, cmd)["err"]) def reserve_dev(options, dev): cmd = options["--mpathpersist-path"] + " -o --reserv --prout-type=5 --param-rk=" + options["--key"] + " -d " + dev return not bool(run_cmd(options, cmd)["err"]) def get_reservation_key(options, dev): cmd = options["--mpathpersist-path"] + " -i -r -d " + dev out = run_cmd(options, cmd) if out["err"]: fail_usage("Cannot get reservation key") match = re.search(r"\s+key\s*=\s*0x(\S+)\s+", out["out"], re.IGNORECASE) return match.group(1) if match else None def get_registration_keys(options, dev): keys = [] cmd = options["--mpathpersist-path"] + " -i -k -d " + dev out = run_cmd(options, cmd) if out["err"]: fail_usage("Cannot get registration keys") for line in out["out"].split("\n"): match = re.search(r"\s+0x(\S+)\s*", line) if match: keys.append(match.group(1)) return keys def dev_write(options, dev): file_path = options["--store-path"] + "/mpath.devices" if not os.path.isdir(options["--store-path"]): os.makedirs(options["--store-path"]) try: store_fh = open(file_path, "a+") except IOError: fail_usage("Failed: Cannot open file \""+ file_path + "\"") out = store_fh.read() if not re.search(r"^" + dev + r"\s+", out): store_fh.write(dev + "\t" + options["--key"] + "\n") store_fh.close() def dev_read(options): dev_key = {} file_path = options["--store-path"] + "/mpath.devices" try: store_fh = open(file_path, "r") except IOError: fail_usage("Failed: Cannot open file \"" + file_path + "\"") # get not empty lines from file for (device, key) in [line.strip().split() for line in store_fh if line.strip()]: dev_key[device] = key store_fh.close() return dev_key def define_new_opts(): all_opt["devices"] = { "getopt" : "d:", "longopt" : "devices", "help" : "-d, --devices=[devices] List of devices to use for current operation", "required" : "1", "shortdesc" : "List of devices to use for current operation. Devices can \ be comma-separated list of device-mapper multipath devices (eg. /dev/mapper/3600508b400105df70000e00000ac0000 or /dev/mapper/mpath1). \ Each device must support SCSI-3 persistent reservations.", "order": 1 } all_opt["key"] = { "getopt" : "k:", "longopt" : "key", "help" : "-k, --key=[key] Key to use for the current operation", "required" : "1", "shortdesc" : "Key to use for the current operation. This key should be \ unique to a node and have to be written in /etc/multipath.conf. For the \"on\" action, the key specifies the key use to \ register the local node. For the \"off\" action, this key specifies the key to \ be removed from the device(s).", "order": 1 } all_opt["mpathpersist_path"] = { "getopt" : ":", "longopt" : "mpathpersist-path", "help" : "--mpathpersist-path=[path] Path to mpathpersist binary", "required" : "0", "shortdesc" : "Path to mpathpersist binary", "default" : "@MPATH_PATH@", "order": 200 } all_opt["store_path"] = { "getopt" : ":", "longopt" : "store-path", "help" : "--store-path=[path] Path to directory containing cached keys", "required" : "0", "shortdesc" : "Path to directory where fence agent can store information", "default" : "@STORE_PATH@", "order": 200 } def main(): atexit.register(atexit_handler) device_opt = ["no_login", "no_password", "devices", "key", "sudo", \ "fabric_fencing", "on_target", "store_path", "mpathpersist_path", "force_on"] define_new_opts() options = check_input(device_opt, process_input(device_opt), other_conditions=True) docs = {} docs["shortdesc"] = "Fence agent for multipath persistent reservation" docs["longdesc"] = "fence_mpath is an I/O fencing agent that uses SCSI-3 \ persistent reservations to control access multipath devices. Underlying \ devices must support SCSI-3 persistent reservations (SPC-3 or greater) as \ well as the \"preempt-and-abort\" subcommand.\nThe fence_mpath agent works by \ having a unique key for each node that has to be set in /etc/multipath.conf. \ Once registered, a single node will become the reservation holder \ by creating a \"write exclusive, registrants only\" reservation on the \ device(s). The result is that only registered nodes may write to the \ device(s). When a node failure occurs, the fence_mpath agent will remove the \ key belonging to the failed node from the device(s). The failed node will no \ longer be able to write to the device(s). A manual reboot is required." docs["vendorurl"] = "https://www.sourceware.org/dm/" show_docs(options, docs) run_delay(options) # Input control BEGIN if not "--key" in options: fail_usage("Failed: key is required") if options["--action"] == "validate-all": sys.exit(0) options["devices"] = options["--devices"].split(",") if not options["devices"]: fail_usage("Failed: No devices found") # Input control END result = fence_action(None, options, set_status, get_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/netio/fence_netio.py b/fence/agents/netio/fence_netio.py index 6dc6a1ad..4fb59cff 100755 --- a/fence/agents/netio/fence_netio.py +++ b/fence/agents/netio/fence_netio.py @@ -1,100 +1,94 @@ #!@PYTHON@ -tt import sys, re, pexpect import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fspawn, fail, EC_LOGIN_DENIED, run_delay -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - def get_power_status(conn, options): conn.send_eol("port %s" % options["--plug"]) re_status = re.compile("250 [01imt]") conn.log_expect(re_status, int(options["--shell-timeout"])) status = { "0" : "off", "1" : "on", "i" : "reboot", "m" : "manual", "t" : "timer" }[conn.after.split()[1]] return status def set_power_status(conn, options): action = { "on" : "1", "off" : "0", "reboot" : "i" }[options["--action"]] conn.send_eol("port %s %s" % (options["--plug"], action)) conn.log_expect("250 OK", int(options["--shell-timeout"])) def get_outlet_list(conn, options): result = {} try: # the NETIO-230B has 4 ports, counting start at 1 for plug in ["1", "2", "3", "4"]: conn.send_eol("port setup %s" % plug) conn.log_expect("250 .+", int(options["--shell-timeout"])) # the name is enclosed in "", drop those with [1:-1] name = conn.after.split()[1][1:-1] result[plug] = (name, "unknown") except Exception as exn: print(str(exn)) return result def main(): device_opt = ["ipaddr", "login", "passwd", "port", "telnet"] atexit.register(atexit_handler) all_opt["ipport"]["default"] = "1234" opt = process_input(device_opt) opt["eol"] = "\r\n" options = check_input(device_opt, opt) docs = {} docs["shortdesc"] = "I/O Fencing agent for Koukaam NETIO-230B" docs["longdesc"] = "fence_netio is an I/O Fencing agent which can be \ used with the Koukaam NETIO-230B Power Distribution Unit. It logs into \ device via telnet and reboots a specified outlet. Lengthy telnet connections \ should be avoided while a GFS cluster is running because the connection will \ block any necessary fencing actions." docs["vendorurl"] = "http://www.koukaam.se/" show_docs(options, docs) ## ## Operate the fencing device ## We can not use fence_login(), username and passwd are sent on one line #### run_delay(options) try: conn = fspawn(options, options["--telnet-path"]) conn.send("set binary\n") conn.send("open %s -%s\n"%(options["--ip"], options["--ipport"])) conn.read_nonblocking(size=100, timeout=int(options["--shell-timeout"])) conn.log_expect("100 HELLO .*", int(options["--shell-timeout"])) conn.send_eol("login %s %s" % (options["--username"], options["--password"])) conn.log_expect("250 OK", int(options["--shell-timeout"])) except pexpect.EOF: fail(EC_LOGIN_DENIED) except pexpect.TIMEOUT: fail(EC_LOGIN_DENIED) result = fence_action(conn, options, set_power_status, get_power_status, get_outlet_list) fence_logout(conn, "quit\n") sys.exit(result) if __name__ == "__main__": main() diff --git a/fence/agents/powerman/fence_powerman.py b/fence/agents/powerman/fence_powerman.py index d9ab8495..962fdb8f 100755 --- a/fence/agents/powerman/fence_powerman.py +++ b/fence/agents/powerman/fence_powerman.py @@ -1,265 +1,257 @@ #!/usr/bin/env python import os import time from datetime import datetime import sys import subprocess import re import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import is_executable, fail_usage, run_delay import logging - -#BEGIN_VERSION_GENERATION -RELEASE_VERSION="Powerman Fencing Agent" -REDHAT_COPYRIGHT="" -BUILD_DATE="" -#END_VERSION_GENERATION - - #### important!!! ####### class PowerMan: """Python wrapper for calling powerman commands This class makes calls to a powerman deamon for a cluster of computers. The make-up of such a call looks something like: $ pm -h elssd1:10101