diff -uNr a/configure.ac b/configure.ac --- a/configure.ac 2017-10-24 11:38:01.084912422 +0200 +++ b/configure.ac 2017-10-24 11:34:55.939413105 +0200 @@ -309,6 +309,7 @@ fence/agents/virsh/Makefile fence/agents/vmware/Makefile fence/agents/vmware_soap/Makefile + fence/agents/vmware_rest/Makefile fence/agents/wti/Makefile fence/agents/xenapi/Makefile fence/agents/hds_cb/Makefile diff -uNr a/fence/agents/vmware_rest/fence_vmware_rest.py b/fence/agents/vmware_rest/fence_vmware_rest.py --- a/fence/agents/vmware_rest/fence_vmware_rest.py 1970-01-01 01:00:00.000000000 +0100 +++ b/fence/agents/vmware_rest/fence_vmware_rest.py 2017-10-24 12:17:10.919982326 +0200 @@ -0,0 +1,189 @@ +#!/usr/bin/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 + +#BEGIN_VERSION_GENERATION +RELEASE_VERSION="" +REDHAT_COPYRIGHT="" +BUILD_DATE="" +#END_VERSION_GENERATION + +state = {"POWERED_ON": "on", 'POWERED_OFF': "off"} + +def get_power_status(conn, options): + res = send_command(conn, "vcenter/vm?filter.names={}".format(options["--plug"]))["value"] + + 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"]] + + send_command(conn, "vcenter/vm/{}/power/{}".format(options["id"], action), "POST") + +def get_list(conn, options): + outlets = {} + + res = send_command(conn, "vcenter/vm") + + 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): + send_command(conn, "com/vmware/cis/session", "DELETE") + 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 Exception(e[1]) + + rc = conn.getinfo(pycurl.HTTP_CODE) + result = web_buffer.getvalue().decode() + + web_buffer.close() + + if len(result) > 0: + result = json.loads(result) + + if rc != 200: + raise Exception("{}: {}".format(rc, result["value"]["messages"][0]["default_message"])) + + 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} + + +def main(): + device_opt = [ + "ipaddr", + "api_path", + "login", + "passwd", + "ssl", + "notls", + "web", + "port", + ] + + 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["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 -uNr a/fence/agents/vmware_rest/Makefile.am b/fence/agents/vmware_rest/Makefile.am --- a/fence/agents/vmware_rest/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ b/fence/agents/vmware_rest/Makefile.am 2017-10-24 11:32:17.369693405 +0200 @@ -0,0 +1,20 @@ +MAINTAINERCLEANFILES = Makefile.in + +TARGET = fence_vmware_rest + +SRC = $(TARGET).py + +EXTRA_DIST = $(SRC) + +sbin_SCRIPTS = $(TARGET) + +man_MANS = $(TARGET).8 + +FENCE_TEST_ARGS = -l test -p test -a test -n 1 + +include $(top_srcdir)/make/fencebuild.mk +include $(top_srcdir)/make/fenceman.mk +include $(top_srcdir)/make/agentpycheck.mk + +clean-local: clean-man + rm -f $(TARGET) diff -uNr a/tests/data/metadata/fence_vmware_rest.xml b/tests/data/metadata/fence_vmware_rest.xml --- a/tests/data/metadata/fence_vmware_rest.xml 1970-01-01 01:00:00.000000000 +0100 +++ b/tests/data/metadata/fence_vmware_rest.xml 2017-10-24 11:35:43.501027721 +0200 @@ -0,0 +1,172 @@ + + +fence_vmware_rest is an I/O Fencing agent which can be used with VMware API to fence virtual machines. +https://www.vmware.com + + + + + TCP/UDP port to use for connection with device + + + + + Disable TLS negotiation, force SSL 3.0 + + + + + SSL connection with verifying fence device's certificate + + + + + Physical plug number, name of virtual machine or UUID + + + + + Forces agent to use IPv6 addresses only + + + + + IP Address or Hostname + + + + + Forces agent to use IPv4 addresses only + + + + + Script to retrieve password + + + + + Login password or passphrase + + + + + SSL connection + + + + + SSL connection without verifying fence device's certificate + + + + + Fencing Action + + + + + Login Name + + + + + Physical plug number, name of virtual machine or UUID + + + + + Login Name + + + + + IP Address or Hostname + + + + + Login password or passphrase + + + + + Script to retrieve password + + + + The path part of the API URL + + + + + 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 operation list + + + + + Wait X seconds after issuing ON/OFF + + + + + Wait X seconds for cmd prompt after login + + + + + Test X seconds for status change after ON/OFF + + + + + Wait X seconds before fencing is started + + + + + Wait X seconds for cmd prompt after issuing command + + + + + Count of attempts to retry power on + + + + + + + + + + + + + +