diff --git a/agents/amt/fence_amt.py b/agents/amt/fence_amt.py index feec6e3e..80d3f74c 100644 --- a/agents/amt/fence_amt.py +++ b/agents/amt/fence_amt.py @@ -1,128 +1,132 @@ #!@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 +try: + from shlex import quote +except ImportError: + from pipes import quote + 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/agents/ipmilan/fence_ipmilan.py b/agents/ipmilan/fence_ipmilan.py index f751de69..0acf977d 100644 --- a/agents/ipmilan/fence_ipmilan.py +++ b/agents/ipmilan/fence_ipmilan.py @@ -1,233 +1,237 @@ #!@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 +try: + from shlex import quote +except ImportError: + from pipes import quote + 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"]) if "--ipmitool-timeout" in options: Cmd.append(" -N " + options["--ipmitool-timeout"]) # --action / -o Cmd.append(" chassis power " + action) # --verbose-level if options["--verbose-level"] > 1: Cmd.append(" -" + "v" * (options["--verbose-level"] - 1)) 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["ipmitool_timeout"] = { "getopt" : ":", "longopt" : "ipmitool-timeout", "help" : "--ipmitool-timeout=[timeout] Timeout (sec) for IPMI operation", "required" : "0", "shortdesc" : "Timeout (sec) for IPMI operation", "default" : "2", "order": 201 } 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", "ipmitool_timeout", "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["lanplus"]["default"] = "1" elif os.path.basename(sys.argv[0]) == "fence_ilo4": all_opt["lanplus"]["default"] = "1" elif os.path.basename(sys.argv[0]) == "fence_ilo5": all_opt["lanplus"]["default"] = "1" elif os.path.basename(sys.argv[0]) == "fence_ipmilanplus": all_opt["lanplus"]["default"] = "1" all_opt["ipport"]["default"] = "623" all_opt["method"]["help"] = "-m, --method=[method] Method to fence (onoff|cycle) (Default: onoff)\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_ilo5", "Fence agent for HP iLO5"), ("fence_ipmilanplus", "Fence agent for IPMIv2 lanplus"), ("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/agents/ironic/fence_ironic.py b/agents/ironic/fence_ironic.py index 66d84fca..d0c9d9c1 100644 --- a/agents/ironic/fence_ironic.py +++ b/agents/ironic/fence_ironic.py @@ -1,130 +1,134 @@ #!@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 +try: + from shlex import quote +except ImportError: + from pipes import quote + 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()