diff --git a/agents/vmware_rest/fence_vmware_rest.py b/agents/vmware_rest/fence_vmware_rest.py index 1505ffe6..6daff121 100644 --- a/agents/vmware_rest/fence_vmware_rest.py +++ b/agents/vmware_rest/fence_vmware_rest.py @@ -1,202 +1,220 @@ #!@PYTHON@ -tt import sys import pycurl, io, json import logging import atexit sys.path.append("@FENCEAGENTSLIBDIR@") from fencing import * from fencing import fail, run_delay, EC_LOGIN_DENIED, EC_STATUS state = {"POWERED_ON": "on", 'POWERED_OFF': "off", 'SUSPENDED': "off"} def get_power_status(conn, options): try: res = send_command(conn, "vcenter/vm?filter.names={}".format(options["--plug"]))["value"] except Exception as e: logging.debug("Failed: {}".format(e)) fail(EC_STATUS) if len(res) == 0: fail(EC_STATUS) options["id"] = res[0]["vm"] result = res[0]["power_state"] return state[result] def set_power_status(conn, options): action = { "on" : "start", "off" : "stop" }[options["--action"]] try: send_command(conn, "vcenter/vm/{}/power/{}".format(options["id"], action), "POST") except Exception as e: logging.debug("Failed: {}".format(e)) fail(EC_STATUS) def get_list(conn, options): outlets = {} try: - res = send_command(conn, "vcenter/vm") + command = "vcenter/vm" + if "--filter" in options: + command = command + "?" + options["--filter"] + res = send_command(conn, command) except: logging.debug("Failed: {}".format(e)) fail(EC_STATUS) for r in res["value"]: outlets[r["name"]] = ("", state[r["power_state"]]) return outlets def connect(opt): conn = pycurl.Curl() ## setup correct URL if "--ssl" in opt or "--ssl-secure" in opt or "--ssl-insecure" in opt: conn.base_url = "https:" else: conn.base_url = "http:" if "--api-path" in opt: api_path = opt["--api-path"] else: api_path = "/rest" conn.base_url += "//" + opt["--ip"] + ":" + str(opt["--ipport"]) + api_path + "/" ## send command through pycurl conn.setopt(pycurl.HTTPHEADER, [ "Accept: application/json", ]) conn.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC) conn.setopt(pycurl.USERPWD, opt["--username"] + ":" + opt["--password"]) conn.setopt(pycurl.TIMEOUT, int(opt["--shell-timeout"])) if "--ssl" in opt or "--ssl-secure" in opt: conn.setopt(pycurl.SSL_VERIFYPEER, 1) conn.setopt(pycurl.SSL_VERIFYHOST, 2) if "--ssl-insecure" in opt: conn.setopt(pycurl.SSL_VERIFYPEER, 0) conn.setopt(pycurl.SSL_VERIFYHOST, 0) try: result = send_command(conn, "com/vmware/cis/session", "POST") except Exception as e: logging.debug("Failed: {}".format(e)) fail(EC_LOGIN_DENIED) # set session id for later requests conn.setopt(pycurl.HTTPHEADER, [ "Accept: application/json", "vmware-api-session-id: {}".format(result["value"]), ]) return conn def disconnect(conn): try: send_command(conn, "com/vmware/cis/session", "DELETE") except Exception as e: logging.debug("Failed: {}".format(e)) conn.close() def send_command(conn, command, method="GET"): url = conn.base_url + command conn.setopt(pycurl.URL, url.encode("ascii")) web_buffer = io.BytesIO() if method == "GET": conn.setopt(pycurl.POST, 0) if method == "POST": conn.setopt(pycurl.POSTFIELDS, "") if method == "DELETE": conn.setopt(pycurl.CUSTOMREQUEST, "DELETE") conn.setopt(pycurl.WRITEFUNCTION, web_buffer.write) try: conn.perform() except Exception as e: raise(e) rc = conn.getinfo(pycurl.HTTP_CODE) result = web_buffer.getvalue().decode("UTF-8") web_buffer.close() if len(result) > 0: result = json.loads(result) if rc != 200: if len(result) > 0: raise Exception("{}: {}".format(rc, result["value"]["messages"][0]["default_message"])) else: raise Exception("Remote returned {} for request to {}".format(rc, url)) logging.debug("url: {}".format(url)) logging.debug("method: {}".format(method)) logging.debug("response code: {}".format(rc)) logging.debug("result: {}\n".format(result)) return result def define_new_opts(): all_opt["api_path"] = { "getopt" : ":", "longopt" : "api-path", "help" : "--api-path=[path] The path part of the API URL", "default" : "/rest", "required" : "0", "shortdesc" : "The path part of the API URL", "order" : 2} + all_opt["filter"] = { + "getopt" : ":", + "longopt" : "filter", + "help" : "--filter=[filter] Filter to only return relevant VMs" + " (e.g. \"filter.names=node1&filter.names=node2\").", + "default" : "", + "required" : "0", + "shortdesc" : "Filter to only return relevant VMs. It can be used to avoid " + "the agent failing when more than 1000 VMs should be returned.", + "order" : 2} def main(): device_opt = [ "ipaddr", "api_path", "login", "passwd", "ssl", "notls", "web", "port", + "filter", ] atexit.register(atexit_handler) define_new_opts() all_opt["shell_timeout"]["default"] = "5" all_opt["power_wait"]["default"] = "1" options = check_input(device_opt, process_input(device_opt)) docs = {} docs["shortdesc"] = "Fence agent for VMware REST API" - docs["longdesc"] = "fence_vmware_rest is an I/O Fencing agent which can be \ -used with VMware API to fence virtual machines." + docs["longdesc"] = """fence_vmware_rest is an I/O Fencing agent which can be \ +used with VMware API to fence virtual machines. + +NOTE: If there's more than 1000 VMs there is a filter parameter to work around \ +the API limit. See https://code.vmware.com/apis/62/vcenter-management#/VM%20/get_vcenter_vm \ +for full list of filters.""" docs["vendorurl"] = "https://www.vmware.com" show_docs(options, docs) #### ## Fence operations #### run_delay(options) conn = connect(options) atexit.register(disconnect, conn) result = fence_action(conn, options, set_power_status, get_power_status, get_list) sys.exit(result) if __name__ == "__main__": main() diff --git a/tests/data/metadata/fence_vmware_rest.xml b/tests/data/metadata/fence_vmware_rest.xml index 5b497a6a..d60c8775 100644 --- a/tests/data/metadata/fence_vmware_rest.xml +++ b/tests/data/metadata/fence_vmware_rest.xml @@ -1,172 +1,179 @@ -fence_vmware_rest is an I/O Fencing agent which can be used with VMware API to fence virtual machines. +fence_vmware_rest is an I/O Fencing agent which can be used with VMware API to fence virtual machines. + +NOTE: If there's more than 1000 VMs there is a filter parameter to work around the API limit. See https://code.vmware.com/apis/62/vcenter-management#/VM%20/get_vcenter_vm for full list of filters. https://www.vmware.com Fencing action IP address or hostname of fencing device IP address or hostname of fencing device TCP/UDP port to use for connection with device Login name Disable TLS negotiation and force SSL3.0. This should only be used for devices that do not support TLS1.0 and up. Login password or passphrase Script to run to retrieve password Login password or passphrase Script to run to retrieve password Physical plug number on device, UUID or identification of machine Physical plug number on device, UUID or identification of machine Use SSL connection with verifying certificate Use SSL connection without verifying certificate Use SSL connection with verifying certificate Login name The path part of the API URL + + + + Filter to only return relevant VMs. It can be used to avoid the agent failing when more than 1000 VMs should be returned. + Disable logging to stderr. Does not affect --verbose or --debug-file or logging to syslog. Verbose mode Write debug information to given file Write debug information to given file Display version information and exit Display help and exit Separator for CSV created by 'list' operation Wait X seconds before fencing is started Wait X seconds for cmd prompt after login Test X seconds for status change after ON/OFF Wait X seconds after issuing ON/OFF Wait X seconds for cmd prompt after issuing command Count of attempts to retry power on Path to gnutls-cli binary