From 98fec5c6d55369ad681abc0cde0d8677835957ab Mon Sep 17 00:00:00 2001 From: Arnold Beilmann Date: Thu, 5 May 2022 15:26:22 +0200 Subject: [PATCH 1/2] modified for PowerVS --- agents/ibm_powervs/fence_ibm_powervs.py | 108 +++++++++++++++++++----- 1 file changed, 89 insertions(+), 19 deletions(-) diff --git a/agents/ibm_powervs/fence_ibm_powervs.py b/agents/ibm_powervs/fence_ibm_powervs.py index 6649771ea..727009220 100755 --- a/agents/ibm_powervs/fence_ibm_powervs.py +++ b/agents/ibm_powervs/fence_ibm_powervs.py @@ -1,10 +1,11 @@ -#!@PYTHON@ -tt +#!/usr/libexec/platform-python -tt import sys import pycurl, io, json import logging import atexit -sys.path.append("@FENCEAGENTSLIBDIR@") +import time +sys.path.append("/usr/share/fence") from fencing import * from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS @@ -14,16 +15,30 @@ "ERROR": "unknown" } +def get_token(conn, options): + + try: + command = "identity/token" + action = "grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&apikey={}".format(options["--token"]) + res = send_command(conn, command, "POST", action, printResult=False) + except Exception as e: + logging.debug("Failed: {}".format(e)) + return "TOKEN_IS_MISSING_OR_WRONG" + + #if "--verbose" in options: + # logging.debug(json.dumps(res, indent=2)) + + return res["access_token"] + def get_list(conn, options): outlets = {} - + try: command = "cloud-instances/{}/pvm-instances".format(options["--instance"]) res = send_command(conn, command) except Exception as e: logging.debug("Failed: {}".format(e)) return outlets - for r in res["pvmInstances"]: if "--verbose" in options: logging.debug(json.dumps(r, indent=2)) @@ -32,6 +47,7 @@ def get_list(conn, options): return outlets def get_power_status(conn, options): + try: command = "cloud-instances/{}/pvm-instances/{}".format( options["--instance"], options["--plug"]) @@ -40,10 +56,11 @@ def get_power_status(conn, options): except KeyError as e: logging.debug("Failed: Unable to get status for {}".format(e)) fail(EC_STATUS) - + return result def set_power_status(conn, options): + action = { "on" : '{"action" : "start"}', "off" : '{"action" : "immediate-shutdown"}', @@ -56,35 +73,63 @@ def set_power_status(conn, options): logging.debug("Failed: Unable to set power to {} for {}".format(options["--action"], e)) fail(EC_STATUS) -def connect(opt): +def connect(opt, token): conn = pycurl.Curl() ## setup correct URL - conn.base_url = "https://" + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" + conn.base_url = "https://private." + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" + if opt["--api-type"] == "public": + conn.base_url = "https://" + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" if opt["--verbose-level"] > 1: - conn.setopt(pycurl.VERBOSE, 1) + conn.setopt(pycurl.VERBOSE, 0) + conn.setopt(pycurl.CONNECTTIMEOUT,int(opt["--shell-timeout"])) conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"])) conn.setopt(pycurl.SSL_VERIFYPEER, 1) conn.setopt(pycurl.SSL_VERIFYHOST, 2) + conn.setopt(pycurl.PROXY, "{}".format(opt["--proxy"])) # set auth token for later requests conn.setopt(pycurl.HTTPHEADER, [ "Content-Type: application/json", - "Authorization: Bearer {}".format(opt["--token"]), + "Authorization: Bearer {}".format(token), "CRN: {}".format(opt["--crn"]), "User-Agent: curl", ]) + + return conn + +def auth_connect(opt): + conn = pycurl.Curl() + + # setup correct URL + conn.base_url = "https://iam.cloud.ibm.com/" + + if opt["--verbose-level"] > 1: + conn.setopt(pycurl.VERBOSE, 1) + + conn.setopt(pycurl.CONNECTTIMEOUT,int(opt["--shell-timeout"])) + conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"])) + conn.setopt(pycurl.SSL_VERIFYPEER, 1) + conn.setopt(pycurl.SSL_VERIFYHOST, 2) + conn.setopt(pycurl.PROXY, "{}".format(opt["--proxy"])) + + # set auth token for later requests + conn.setopt(pycurl.HTTPHEADER, [ + "Content-type: application/x-www-form-urlencoded", + "Accept: application/json", + "User-Agent: curl", + ]) return conn def disconnect(conn): conn.close() -def send_command(conn, command, method="GET", action=None): +def send_command(conn, command, method="GET", action=None, printResult=True): url = conn.base_url + command - + conn.setopt(pycurl.URL, url.encode("ascii")) web_buffer = io.BytesIO() @@ -99,8 +144,10 @@ def send_command(conn, command, method="GET", action=None): conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write) try: + time.sleep(3) conn.perform() except Exception as e: + logging.error("ADD_DEBUG: {}".format(e)) raise(e) rc = conn.getinfo(pycurl.HTTP_CODE) @@ -110,8 +157,7 @@ def send_command(conn, command, method="GET", action=None): if rc != 200: if len(result) > 0: - raise Exception("{}: {}".format(rc, - result["value"]["messages"][0]["default_message"])) + raise Exception("{}: {}".format(rc,result)) else: raise Exception("Remote returned {} for request to {}".format(rc, url)) @@ -121,7 +167,8 @@ def send_command(conn, command, method="GET", action=None): logging.debug("url: {}".format(url)) logging.debug("method: {}".format(method)) logging.debug("response code: {}".format(rc)) - logging.debug("result: {}\n".format(result)) + if printResult: + logging.debug("result: {}\n".format(result)) return result @@ -129,9 +176,9 @@ def define_new_opts(): all_opt["token"] = { "getopt" : ":", "longopt" : "token", - "help" : "--token=[token] Bearer Token", + "help" : "--token=[token] API Token", "required" : "1", - "shortdesc" : "Bearer Token", + "shortdesc" : "API Token", "order" : 0 } all_opt["crn"] = { @@ -158,6 +205,22 @@ def define_new_opts(): "shortdesc" : "Region", "order" : 0 } + all_opt["api-type"] = { + "getopt" : ":", + "longopt" : "api-type", + "help" : "--api-type=[private|public] API-type: 'private' (default) or 'public'", + "required" : "0", + "shortdesc" : "API-type (private|public)", + "order" : 0 + } + all_opt["proxy"] = { + "getopt" : ":", + "longopt" : "proxy", + "help" : "--proxy=[http://:] Proxy: 'http://:'", + "required" : "0", + "shortdesc" : "Network proxy", + "order" : 0 + } def main(): @@ -166,6 +229,8 @@ def main(): "crn", "instance", "region", + "api-type", + "proxy", "port", "no_password", ] @@ -173,9 +238,11 @@ def main(): atexit.register(atexit_handler) define_new_opts() - all_opt["shell_timeout"]["default"] = "15" + all_opt["shell_timeout"]["default"] = "500" all_opt["power_timeout"]["default"] = "30" all_opt["power_wait"]["default"] = "1" + all_opt["api-type"]["default"] = "private" + all_opt["proxy"]["default"] = "" options = check_input(device_opt, process_input(device_opt)) @@ -190,8 +257,11 @@ def main(): ## Fence operations #### run_delay(options) - - conn = connect(options) + + auth_conn = auth_connect(options) + token = get_token(auth_conn, options) + disconnect(auth_conn) + conn = connect(options, token) atexit.register(disconnect, conn) result = fence_action(conn, options, set_power_status, get_power_status, get_list) From fbe9a539ac8f40686a8027b7e768d9f7b799e485 Mon Sep 17 00:00:00 2001 From: Oyvind Albrigtsen Date: Fri, 6 May 2022 11:22:47 +0200 Subject: [PATCH 2/2] fence_ibm_powervs: cleanup and fixes --- agents/ibm_powervs/fence_ibm_powervs.py | 37 ++++++++++------------- tests/data/metadata/fence_ibm_powervs.xml | 19 ++++++++++-- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/agents/ibm_powervs/fence_ibm_powervs.py b/agents/ibm_powervs/fence_ibm_powervs.py index 727009220..819ab8896 100755 --- a/agents/ibm_powervs/fence_ibm_powervs.py +++ b/agents/ibm_powervs/fence_ibm_powervs.py @@ -1,11 +1,11 @@ -#!/usr/libexec/platform-python -tt +#!@PYTHON@ -tt import sys import pycurl, io, json import logging import atexit import time -sys.path.append("/usr/share/fence") +sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS @@ -16,7 +16,6 @@ } def get_token(conn, options): - try: command = "identity/token" action = "grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&apikey={}".format(options["--token"]) @@ -25,20 +24,18 @@ def get_token(conn, options): logging.debug("Failed: {}".format(e)) return "TOKEN_IS_MISSING_OR_WRONG" - #if "--verbose" in options: - # logging.debug(json.dumps(res, indent=2)) - return res["access_token"] def get_list(conn, options): outlets = {} - + try: command = "cloud-instances/{}/pvm-instances".format(options["--instance"]) res = send_command(conn, command) except Exception as e: logging.debug("Failed: {}".format(e)) return outlets + for r in res["pvmInstances"]: if "--verbose" in options: logging.debug(json.dumps(r, indent=2)) @@ -47,7 +44,6 @@ def get_list(conn, options): return outlets def get_power_status(conn, options): - try: command = "cloud-instances/{}/pvm-instances/{}".format( options["--instance"], options["--plug"]) @@ -56,11 +52,10 @@ def get_power_status(conn, options): except KeyError as e: logging.debug("Failed: Unable to get status for {}".format(e)) fail(EC_STATUS) - + return result def set_power_status(conn, options): - action = { "on" : '{"action" : "start"}', "off" : '{"action" : "immediate-shutdown"}', @@ -77,11 +72,11 @@ def connect(opt, token): conn = pycurl.Curl() ## setup correct URL - conn.base_url = "https://private." + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" - if opt["--api-type"] == "public": - conn.base_url = "https://" + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" + conn.base_url = "https://" + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" + if opt["--api-type"] == "private": + conn.base_url = "https://private." + opt["--region"] + ".power-iaas.cloud.ibm.com/pcloud/v1/" - if opt["--verbose-level"] > 1: + if opt["--verbose-level"] < 3: conn.setopt(pycurl.VERBOSE, 0) conn.setopt(pycurl.CONNECTTIMEOUT,int(opt["--shell-timeout"])) @@ -129,7 +124,7 @@ def disconnect(conn): def send_command(conn, command, method="GET", action=None, printResult=True): url = conn.base_url + command - + conn.setopt(pycurl.URL, url.encode("ascii")) web_buffer = io.BytesIO() @@ -144,10 +139,9 @@ def send_command(conn, command, method="GET", action=None, printResult=True): conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write) try: - time.sleep(3) conn.perform() except Exception as e: - logging.error("ADD_DEBUG: {}".format(e)) + logging.error("send_command(): {}".format(e)) raise(e) rc = conn.getinfo(pycurl.HTTP_CODE) @@ -208,9 +202,9 @@ def define_new_opts(): all_opt["api-type"] = { "getopt" : ":", "longopt" : "api-type", - "help" : "--api-type=[private|public] API-type: 'private' (default) or 'public'", + "help" : "--api-type=[public|private] API-type: 'public' (default) or 'private'", "required" : "0", - "shortdesc" : "API-type (private|public)", + "shortdesc" : "API-type (public|private)", "order" : 0 } all_opt["proxy"] = { @@ -238,9 +232,10 @@ def main(): atexit.register(atexit_handler) define_new_opts() - all_opt["shell_timeout"]["default"] = "500" + all_opt["shell_timeout"]["default"] = "15" all_opt["power_timeout"]["default"] = "30" all_opt["power_wait"]["default"] = "1" + all_opt["stonith_status_sleep"]["default"] = "3" all_opt["api-type"]["default"] = "private" all_opt["proxy"]["default"] = "" @@ -257,7 +252,7 @@ def main(): ## Fence operations #### run_delay(options) - + auth_conn = auth_connect(options) token = get_token(auth_conn, options) disconnect(auth_conn) diff --git a/tests/data/metadata/fence_ibm_powervs.xml b/tests/data/metadata/fence_ibm_powervs.xml index fe86331bd..81cea4379 100644 --- a/tests/data/metadata/fence_ibm_powervs.xml +++ b/tests/data/metadata/fence_ibm_powervs.xml @@ -3,6 +3,16 @@ fence_ibm_powervs is an I/O Fencing agent which can be used with IBM PowerVS to fence virtual machines. https://www.ibm.com + + + + API-type (public|private) + + + + + API-type (public|private) + @@ -13,6 +23,11 @@ PowerVS Instance + + + + Network proxy + @@ -21,7 +36,7 @@ - Bearer Token + API Token @@ -110,7 +125,7 @@ - + Sleep X seconds between status calls during a STONITH action