From 7bb38c16a32dd842011337b58ab52631957b9adc Mon Sep 17 00:00:00 2001
From: Andreas Schauberer <74912604+andscha@users.noreply.github.com>
Date: Mon, 19 Jun 2023 10:05:37 +0200
Subject: [PATCH 13/46] fence_ibm_powervs: improved performance (#542)

* fence_ibm_powervs: improved performance

fence_ibm_powervs: improved performance
- improved performance using less power-iaas.cloud.ibm.com API calls
- add support for reboot_cycle, method to fence (onoff|cycle) (Default: onoff)

Addressed comments by oalbrigt in ClusterLabs #PR542
- you can use if options["--verbose-level"] > 1: to only print it when -vv or more or verbose_level is set to 2 or higher.
---
 agents/ibm_powervs/fence_ibm_powervs.py   | 70 +++++++++++++++++------
 tests/data/metadata/fence_ibm_powervs.xml |  8 +++
 2 files changed, 59 insertions(+), 19 deletions(-)

diff --git a/agents/ibm_powervs/fence_ibm_powervs.py b/agents/ibm_powervs/fence_ibm_powervs.py
index 18389361..e65462cb 100755
--- a/agents/ibm_powervs/fence_ibm_powervs.py
+++ b/agents/ibm_powervs/fence_ibm_powervs.py
@@ -12,6 +12,8 @@ from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS
 state = {
 	 "ACTIVE": "on",
 	 "SHUTOFF": "off",
+	 "HARD_REBOOT": "on",
+	 "SOFT_REBOOT": "on",
 	 "ERROR": "unknown"
 }
 
@@ -37,21 +39,30 @@ def get_list(conn, options):
 		return outlets
 
 	for r in res["pvmInstances"]:
-		if "--verbose" in options:
+		if options["--verbose-level"] > 1:
 			logging.debug(json.dumps(r, indent=2))
 		outlets[r["pvmInstanceID"]] = (r["serverName"], state[r["status"]])
 
 	return outlets
 
 def get_power_status(conn, options):
+	outlets = {}
+	logging.debug("Info: getting power status for LPAR " + options["--plug"] + " instance " + options["--instance"])
 	try:
 		command = "cloud-instances/{}/pvm-instances/{}".format(
 				options["--instance"], options["--plug"])
 		res = send_command(conn, command)
-		result = get_list(conn, options)[options["--plug"]][1]
+		outlets[res["pvmInstanceID"]] = (res["serverName"], state[res["status"]])
+		if options["--verbose-level"] > 1:
+			logging.debug(json.dumps(res, indent=2))
+		result = outlets[options["--plug"]][1]
+		logging.debug("Info: Status: {}".format(result))
 	except KeyError as e:
-		logging.debug("Failed: Unable to get status for {}".format(e))
-		fail(EC_STATUS)
+		try:
+			result = get_list(conn, options)[options["--plug"]][1]
+		except KeyError as ex:
+			logging.debug("Failed: Unable to get status for {}".format(ex))
+			fail(EC_STATUS)
 
 	return result
 
@@ -61,6 +72,7 @@ def set_power_status(conn, options):
 		"off" : '{"action" : "immediate-shutdown"}',
 	}[options["--action"]]
 
+	logging.debug("Info: set power status to " + options["--action"] + " for LPAR " + options["--plug"] + " instance " + options["--instance"])
 	try:
 		send_command(conn, "cloud-instances/{}/pvm-instances/{}/action".format(
 				options["--instance"], options["--plug"]), "POST", action)
@@ -68,6 +80,25 @@ def set_power_status(conn, options):
 		logging.debug("Failed: Unable to set power to {} for {}".format(options["--action"], e))
 		fail(EC_STATUS)
 
+def reboot_cycle(conn, options):
+	action = {
+		"reboot" :  '{"action" : "hard-reboot"}',
+	}[options["--action"]]
+
+	logging.debug("Info: start reboot cycle with action " + options["--action"] + " for LPAR " + options["--plug"] + " instance " + options["--instance"])
+	try:
+		send_command(conn, "cloud-instances/{}/pvm-instances/{}/action".format(
+				options["--instance"], options["--plug"]), "POST", action)
+	except Exception as e:
+		result = get_power_status(conn, options)
+		logging.debug("Info: Status {}".format(result))
+		if result == "off":
+			return True
+		else:
+			logging.debug("Failed: Unable to cycle with {} for {}".format(options["--action"], e))
+			fail(EC_STATUS)
+	return True 
+
 def connect(opt, token):
 	conn = pycurl.Curl()
 
@@ -200,21 +231,21 @@ def define_new_opts():
 		"order" : 0
 	}
 	all_opt["api-type"] = {
-                "getopt" : ":",
-                "longopt" : "api-type",
-                "help" : "--api-type=[public|private]          API-type: 'public' (default) or 'private'",
-                "required" : "0",
-                "shortdesc" : "API-type (public|private)",
-                "order" : 0
-        }
+		"getopt" : ":",
+		"longopt" : "api-type",
+		"help" : "--api-type=[public|private]          API-type: 'public' (default) or 'private'",
+		"required" : "0",
+		"shortdesc" : "API-type (public|private)",
+		"order" : 0
+	}
 	all_opt["proxy"] = {
-                "getopt" : ":",
-                "longopt" : "proxy",
-                "help" : "--proxy=[http://<URL>:<PORT>]          Proxy: 'http://<URL>:<PORT>'",
-                "required" : "0",
-                "shortdesc" : "Network proxy",
-                "order" : 0
-        }
+		"getopt" : ":",
+		"longopt" : "proxy",
+		"help" : "--proxy=[http://<URL>:<PORT>]          Proxy: 'http://<URL>:<PORT>'",
+		"required" : "0",
+		"shortdesc" : "Network proxy",
+		"order" : 0
+	}
 
 
 def main():
@@ -227,6 +258,7 @@ def main():
 		"proxy",
 		"port",
 		"no_password",
+		"method",
 	]
 
 	atexit.register(atexit_handler)
@@ -259,7 +291,7 @@ used with IBM PowerVS to fence virtual machines."""
 	conn = connect(options, token)
 	atexit.register(disconnect, conn)
 
-	result = fence_action(conn, options, set_power_status, get_power_status, get_list)
+	result = fence_action(conn, options, set_power_status, get_power_status, get_list, reboot_cycle)
 
 	sys.exit(result)
 
diff --git a/tests/data/metadata/fence_ibm_powervs.xml b/tests/data/metadata/fence_ibm_powervs.xml
index 79878a9a..7bdee4e2 100644
--- a/tests/data/metadata/fence_ibm_powervs.xml
+++ b/tests/data/metadata/fence_ibm_powervs.xml
@@ -43,6 +43,14 @@
 		<content type="string" default="reboot"  />
 		<shortdesc lang="en">Fencing action</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="plug" unique="0" required="1" obsoletes="port">
 		<getopt mixed="-n, --plug=[id]" />
 		<content type="string"  />
-- 
2.25.1

