diff --git a/agents/zvm/fence_zvmip.py b/agents/zvm/fence_zvmip.py index 4f538e10..c37950a2 100644 --- a/agents/zvm/fence_zvmip.py +++ b/agents/zvm/fence_zvmip.py @@ -1,226 +1,245 @@ #!@PYTHON@ -tt import sys import atexit import socket import struct import logging sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, fail_usage, run_delay, EC_LOGIN_DENIED, EC_TIMED_OUT INT4 = 4 def open_socket(options): try: if "--inet6-only" in options: protocol = socket.AF_INET6 elif "--inet4-only" in options: protocol = socket.AF_INET else: protocol = 0 (_, _, _, _, addr) = socket.getaddrinfo( \ options["--ip"], options["--ipport"], protocol, 0, socket.IPPROTO_TCP, socket.AI_PASSIVE )[0] except socket.gaierror: fail(EC_LOGIN_DENIED) if "--ssl-secure" in options or "--ssl-insecure" in options: import ssl sock = socket.socket() sslcx = ssl.create_default_context() if "--ssl-insecure" in options: sslcx.check_hostname = False sslcx.verify_mode = ssl.CERT_NONE conn = sslcx.wrap_socket(sock, server_hostname=options["--ip"]) else: conn = socket.socket() conn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) conn.settimeout(float(options["--shell-timeout"]) or None) try: conn.connect(addr) except socket.error as e: logging.debug(e) fail(EC_LOGIN_DENIED) return conn def smapi_pack_string(string): return struct.pack("!i%ds" % (len(string)), len(string), string.encode("UTF-8")) def prepare_smapi_command(options, smapi_function, additional_args): packet_size = 3*INT4 + len(smapi_function) + len(options["--username"]) + len(options["--password"]) for arg in additional_args: packet_size += INT4 + len(arg) command = struct.pack("!i", packet_size) command += smapi_pack_string(smapi_function) command += smapi_pack_string(options["--username"]) command += smapi_pack_string(options["--password"]) for arg in additional_args: command += smapi_pack_string(arg) return command def get_power_status(conn, options): del conn if options.get("--original-action", None) == "monitor": (return_code, reason_code, images_active) = \ get_list_of_images(options, "Check_Authentication", None) logging.debug("Check_Authenticate (%d,%d)", return_code, reason_code) if return_code == 0: return {} else: fail(EC_LOGIN_DENIED) if options["--action"] == "list": # '*' = list all active images options["--plug"] = "*" (return_code, reason_code, images_active) = \ get_list_of_images(options, "Image_Status_Query", options["--plug"]) logging.debug("Image_Status_Query results are (%d,%d)", return_code, reason_code) if not options["--action"] == "list": if (return_code == 0) and (reason_code == 0): return "on" elif (return_code == 0) and (reason_code == 12): # We are running always with --missing-as-off because we can not check if image # is defined or not (look at rhbz#1188750) return "off" else: return "unknown" else: (return_code, reason_code, images_defined) = \ get_list_of_images(options, "Image_Name_Query_DM", options["--username"]) logging.debug("Image_Name_Query_DM results are (%d,%d)", return_code, reason_code) return dict([(i, ("", "on" if i in images_active else "off")) for i in images_defined]) def set_power_status(conn, options): conn = open_socket(options) packet = None if options["--action"] == "on": packet = prepare_smapi_command(options, "Image_Activate", [options["--plug"]]) elif options["--action"] == "off": packet = prepare_smapi_command(options, "Image_Deactivate", [options["--plug"], "IMMED"]) conn.send(packet) request_id = struct.unpack("!i", conn.recv(INT4))[0] (output_len, request_id, return_code, reason_code) = struct.unpack("!iiii", conn.recv(INT4 * 4)) logging.debug("Image_(De)Activate results are (%d,%d)", return_code, reason_code) conn.close() return def get_list_of_images(options, command, data_as_plug): conn = open_socket(options) if data_as_plug is None: packet = prepare_smapi_command(options, command, []) else: packet = prepare_smapi_command(options, command, [data_as_plug]) conn.send(packet) try: request_id = struct.unpack("!i", conn.recv(INT4))[0] (output_len, request_id, return_code, reason_code) = struct.unpack("!iiii", conn.recv(INT4 * 4)) except struct.error: logging.debug(sys.exc_info()) fail_usage("Failed: Unable to connect to {} port: {} SSL: {} \n".format(options["--ip"], options["--ipport"], bool("--ssl" in options))) images = set() if output_len > 3*INT4: recvflag = socket.MSG_WAITALL if "--ssl-secure" not in options and "--ssl-insecure" not in options else 0 array_len = struct.unpack("!i", conn.recv(INT4))[0] data = "" while True: read_data = conn.recv(1024, recvflag).decode("UTF-8") data += read_data if array_len == len(data): break elif not read_data: logging.error("Failed: Not enough data read from socket") fail(EC_TIMED_OUT) parsed_len = 0 while parsed_len < array_len: string_len = struct.unpack("!i", data[parsed_len:parsed_len+INT4].encode("UTF-8"))[0] parsed_len += INT4 image_name = struct.unpack("!%ds" % (string_len), data[parsed_len:parsed_len+string_len].encode("UTF-8"))[0].decode("UTF-8") parsed_len += string_len images.add(image_name) conn.close() return (return_code, reason_code, images) def define_new_opts(): all_opt["disable_ssl"] = { "getopt" : "", "longopt" : "disable-ssl", "help" : "--disable-ssl Don't use SSL connection", "required" : "0", "shortdesc" : "Don't use SSL", "order": 2 } def main(): device_opt = ["ipaddr", "login", "passwd", "port", "method", "missing_as_off", "inet4_only", "inet6_only", "ssl", "disable_ssl"] atexit.register(atexit_handler) define_new_opts() all_opt["ssl"]["help"] = "-z, --ssl Use SSL connection with verifying certificate (Default)" all_opt["ipport"]["default"] = "44444" all_opt["shell_timeout"]["default"] = "5" all_opt["missing_as_off"]["default"] = "1" all_opt["ssl"]["default"] = "1" options = check_input(device_opt, process_input(device_opt), other_conditions=True) if "--disable-ssl" in options or options["--ssl"] == "0": for k in ["--ssl", "--ssl-secure", "--ssl-insecure"]: if k in options: del options[k] if len(options.get("--plug", "")) > 8: fail_usage("Failed: Name of image can not be longer than 8 characters") if options["--action"] == "validate-all": sys.exit(0) docs = {} docs["shortdesc"] = "Fence agent for use with z/VM Virtual Machines" - docs["longdesc"] = """The fence_zvm agent is intended to be used with with z/VM SMAPI service via TCP/IP + docs["longdesc"] = """The fence_zvmip agent is intended to be used with the +z/VM SMAPI service via TCP/IP. -To use this agent the z/VM SMAPI service needs to be configured to allow the virtual machine running this agent to connect to it and issue -the image_recycle operation. This involves updating the VSMWORK1 AUTHLIST VMSYS:VSMWORK1. file. The entry should look something similar to -this: +The z/VM SMAPI service must be configured so that the virtual machine running +the agent can connect to the service, access the system's directory manager, +and shortly thereafter run image_deactivate and image_activate. This involves +updating the VSMWORK1 NAMELIST and VSMWORK1 AUTHLIST VMSYS:VSMWORK1 files. + +The NAMELIST entry assigns all the required functions to one nick and should +look similar to this: + +:nick.ZVM_FENCE +:list. +IMAGE_ACTIVATE +IMAGE_DEACTIVATE +IMAGE_STATUS_QUERY +CHECK_AUTHENTICATION +IMAGE_NAME_QUERY_DM + + +The AUTHLIST entry authorizes the user to perform all the functions associated +with the nick, and should look similar to this: Column 1 Column 66 Column 131 - | | | - V V V +| | | +V V V + +XXXXXXXX ALL ZVM_FENCE -XXXXXXXX ALL IMAGE_CHARACTERISTICS +where XXXXXXXX is the name of the user in the authuser field of the request. -Where XXXXXXX is the name of the virtual machine used in the authuser field of the request. This virtual machine also has to be authorized -to access the system's directory manager. +Refer to the official z/VM documentation for complete instructions and +reference materials. """ docs["vendorurl"] = "http://www.ibm.com" show_docs(options, docs) run_delay(options) result = fence_action(None, options, set_power_status, get_power_status, get_power_status) sys.exit(result) if __name__ == "__main__": main() diff --git a/tests/data/metadata/fence_zvmip.xml b/tests/data/metadata/fence_zvmip.xml index 6996ab73..96393bdf 100644 --- a/tests/data/metadata/fence_zvmip.xml +++ b/tests/data/metadata/fence_zvmip.xml @@ -1,225 +1,244 @@ <?xml version="1.0" ?> <resource-agent name="fence_zvmip" shortdesc="Fence agent for use with z/VM Virtual Machines" > -<longdesc>The fence_zvm agent is intended to be used with with z/VM SMAPI service via TCP/IP +<longdesc>The fence_zvmip agent is intended to be used with the +z/VM SMAPI service via TCP/IP. -To use this agent the z/VM SMAPI service needs to be configured to allow the virtual machine running this agent to connect to it and issue -the image_recycle operation. This involves updating the VSMWORK1 AUTHLIST VMSYS:VSMWORK1. file. The entry should look something similar to -this: +The z/VM SMAPI service must be configured so that the virtual machine running +the agent can connect to the service, access the system's directory manager, +and shortly thereafter run image_deactivate and image_activate. This involves +updating the VSMWORK1 NAMELIST and VSMWORK1 AUTHLIST VMSYS:VSMWORK1 files. + +The NAMELIST entry assigns all the required functions to one nick and should +look similar to this: + +:nick.ZVM_FENCE +:list. +IMAGE_ACTIVATE +IMAGE_DEACTIVATE +IMAGE_STATUS_QUERY +CHECK_AUTHENTICATION +IMAGE_NAME_QUERY_DM + + +The AUTHLIST entry authorizes the user to perform all the functions associated +with the nick, and should look similar to this: Column 1 Column 66 Column 131 - | | | - V V V +| | | +V V V + +XXXXXXXX ALL ZVM_FENCE -XXXXXXXX ALL IMAGE_CHARACTERISTICS +where XXXXXXXX is the name of the user in the authuser field of the request. -Where XXXXXXX is the name of the virtual machine used in the authuser field of the request. This virtual machine also has to be authorized -to access the system's directory manager. +Refer to the official z/VM documentation for complete instructions and +reference materials. </longdesc> <vendor-url>http://www.ibm.com</vendor-url> <parameters> <parameter name="action" unique="0" required="1"> <getopt mixed="-o, --action=[action]" /> <content type="string" default="reboot" /> <shortdesc lang="en">Fencing action</shortdesc> </parameter> <parameter name="inet4_only" unique="0" required="0"> <getopt mixed="-4, --inet4-only" /> <content type="boolean" /> <shortdesc lang="en">Forces agent to use IPv4 addresses only</shortdesc> </parameter> <parameter name="inet6_only" unique="0" required="0"> <getopt mixed="-6, --inet6-only" /> <content type="boolean" /> <shortdesc lang="en">Forces agent to use IPv6 addresses only</shortdesc> </parameter> <parameter name="ip" unique="0" required="1" obsoletes="ipaddr"> <getopt mixed="-a, --ip=[ip]" /> <content type="string" /> <shortdesc lang="en">IP address or hostname of fencing device</shortdesc> </parameter> <parameter name="ipaddr" unique="0" required="1" deprecated="1"> <getopt mixed="-a, --ip=[ip]" /> <content type="string" /> <shortdesc lang="en">IP address or hostname of fencing device</shortdesc> </parameter> <parameter name="ipport" unique="0" required="0"> <getopt mixed="-u, --ipport=[port]" /> <content type="integer" default="44444" /> <shortdesc lang="en">TCP/UDP port to use for connection with device</shortdesc> </parameter> <parameter name="login" unique="0" required="1" deprecated="1"> <getopt mixed="-l, --username=[name]" /> <content type="string" /> <shortdesc lang="en">Login name</shortdesc> </parameter> <parameter name="method" unique="0" required="0"> <getopt mixed="-m, --method=[method]" /> <content type="select" default="onoff" > <option value="onoff" /> <option value="cycle" /> </content> <shortdesc lang="en">Method to fence</shortdesc> </parameter> <parameter name="passwd" unique="0" required="0" deprecated="1"> <getopt mixed="-p, --password=[password]" /> <content type="string" /> <shortdesc lang="en">Login password or passphrase</shortdesc> </parameter> <parameter name="passwd_script" unique="0" required="0" deprecated="1"> <getopt mixed="-S, --password-script=[script]" /> <content type="string" /> <shortdesc lang="en">Script to run to retrieve password</shortdesc> </parameter> <parameter name="password" unique="0" required="0" obsoletes="passwd"> <getopt mixed="-p, --password=[password]" /> <content type="string" /> <shortdesc lang="en">Login password or passphrase</shortdesc> </parameter> <parameter name="password_script" unique="0" required="0" obsoletes="passwd_script"> <getopt mixed="-S, --password-script=[script]" /> <content type="string" /> <shortdesc lang="en">Script to run to retrieve password</shortdesc> </parameter> <parameter name="plug" unique="0" required="1" obsoletes="port"> <getopt mixed="-n, --plug=[id]" /> <content type="string" /> <shortdesc lang="en">Physical plug number on device, UUID or identification of machine</shortdesc> </parameter> <parameter name="port" unique="0" required="1" deprecated="1"> <getopt mixed="-n, --plug=[id]" /> <content type="string" /> <shortdesc lang="en">Physical plug number on device, UUID or identification of machine</shortdesc> </parameter> <parameter name="ssl" unique="0" required="0"> <getopt mixed="-z, --ssl" /> <content type="boolean" default="1" /> <shortdesc lang="en">Use SSL connection with verifying certificate (Default)</shortdesc> </parameter> <parameter name="ssl_insecure" unique="0" required="0"> <getopt mixed="--ssl-insecure" /> <content type="boolean" /> <shortdesc lang="en">Use SSL connection without verifying certificate</shortdesc> </parameter> <parameter name="ssl_secure" unique="0" required="0"> <getopt mixed="--ssl-secure" /> <content type="boolean" /> <shortdesc lang="en">Use SSL connection with verifying certificate</shortdesc> </parameter> <parameter name="username" unique="0" required="1" obsoletes="login"> <getopt mixed="-l, --username=[name]" /> <content type="string" /> <shortdesc lang="en">Login name</shortdesc> </parameter> <parameter name="disable_ssl" unique="0" required="0"> <getopt mixed="--disable-ssl" /> <content type="boolean" /> <shortdesc lang="en">Don't use SSL</shortdesc> </parameter> <parameter name="quiet" unique="0" required="0"> <getopt mixed="-q, --quiet" /> <content type="boolean" /> <shortdesc lang="en">Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog.</shortdesc> </parameter> <parameter name="verbose" unique="0" required="0"> <getopt mixed="-v, --verbose" /> <content type="boolean" /> <shortdesc lang="en">Verbose mode. Multiple -v flags can be stacked on the command line (e.g., -vvv) to increase verbosity.</shortdesc> </parameter> <parameter name="verbose_level" unique="0" required="0"> <getopt mixed="--verbose-level" /> <content type="integer" /> <shortdesc lang="en">Level of debugging detail in output. Defaults to the number of --verbose flags specified on the command line, or to 1 if verbose=1 in a stonith device configuration (i.e., on stdin).</shortdesc> </parameter> <parameter name="debug" unique="0" required="0" deprecated="1"> <getopt mixed="-D, --debug-file=[debugfile]" /> <content type="string" /> <shortdesc lang="en">Write debug information to given file</shortdesc> </parameter> <parameter name="debug_file" unique="0" required="0" obsoletes="debug"> <getopt mixed="-D, --debug-file=[debugfile]" /> <shortdesc lang="en">Write debug information to given file</shortdesc> </parameter> <parameter name="version" unique="0" required="0"> <getopt mixed="-V, --version" /> <content type="boolean" /> <shortdesc lang="en">Display version information and exit</shortdesc> </parameter> <parameter name="help" unique="0" required="0"> <getopt mixed="-h, --help" /> <content type="boolean" /> <shortdesc lang="en">Display help and exit</shortdesc> </parameter> <parameter name="plug_separator" unique="0" required="0"> <getopt mixed="--plug-separator=[char]" /> <content type="string" default="," /> <shortdesc lang="en">Separator for plug parameter when specifying more than 1 plug</shortdesc> </parameter> <parameter name="separator" unique="0" required="0"> <getopt mixed="-C, --separator=[char]" /> <content type="string" default="," /> <shortdesc lang="en">Separator for CSV created by 'list' operation</shortdesc> </parameter> <parameter name="delay" unique="0" required="0"> <getopt mixed="--delay=[seconds]" /> <content type="second" default="0" /> <shortdesc lang="en">Wait X seconds before fencing is started</shortdesc> </parameter> <parameter name="disable_timeout" unique="0" required="0"> <getopt mixed="--disable-timeout=[true/false]" /> <content type="string" /> <shortdesc lang="en">Disable timeout (true/false) (default: true when run from Pacemaker 2.0+)</shortdesc> </parameter> <parameter name="login_timeout" unique="0" required="0"> <getopt mixed="--login-timeout=[seconds]" /> <content type="second" default="5" /> <shortdesc lang="en">Wait X seconds for cmd prompt after login</shortdesc> </parameter> <parameter name="missing_as_off" unique="0" required="0"> <getopt mixed="--missing-as-off" /> <content type="boolean" default="1" /> <shortdesc lang="en">Missing port returns OFF instead of failure</shortdesc> </parameter> <parameter name="power_timeout" unique="0" required="0"> <getopt mixed="--power-timeout=[seconds]" /> <content type="second" default="20" /> <shortdesc lang="en">Test X seconds for status change after ON/OFF</shortdesc> </parameter> <parameter name="power_wait" unique="0" required="0"> <getopt mixed="--power-wait=[seconds]" /> <content type="second" default="0" /> <shortdesc lang="en">Wait X seconds after issuing ON/OFF</shortdesc> </parameter> <parameter name="shell_timeout" unique="0" required="0"> <getopt mixed="--shell-timeout=[seconds]" /> <content type="second" default="5" /> <shortdesc lang="en">Wait X seconds for cmd prompt after issuing command</shortdesc> </parameter> <parameter name="stonith_status_sleep" unique="0" required="0"> <getopt mixed="--stonith-status-sleep=[seconds]" /> <content type="second" default="1" /> <shortdesc lang="en">Sleep X seconds between status calls during a STONITH action</shortdesc> </parameter> <parameter name="retry_on" unique="0" required="0"> <getopt mixed="--retry-on=[attempts]" /> <content type="integer" default="1" /> <shortdesc lang="en">Count of attempts to retry power on</shortdesc> </parameter> <parameter name="gnutlscli_path" unique="0" required="0"> <getopt mixed="--gnutlscli-path=[path]" /> <shortdesc lang="en">Path to gnutls-cli binary</shortdesc> </parameter> </parameters> <actions> <action name="on" automatic="0"/> <action name="off" /> <action name="reboot" /> <action name="status" /> <action name="list" /> <action name="list-status" /> <action name="monitor" /> <action name="metadata" /> <action name="manpage" /> <action name="validate-all" /> </actions> </resource-agent>