From cab1e1b5fcf119b34f96829f5c618191b02cbc3e Mon Sep 17 00:00:00 2001
From: Helen Koike <helen.koike@collabora.com>
Date: Sat, 18 Nov 2017 17:10:24 -0200
Subject: [PATCH] fence_gce: new agent

First implementation of GCE agent
---
 .travis.yml                       |   1 +
 configure.ac                      |   3 ++
 fence/agents/gce/fence_gce.py     | 110 ++++++++++++++++++++++++++++++++++++++
 tests/data/metadata/fence_gce.xml | 110 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 224 insertions(+)
 create mode 100644 fence/agents/gce/fence_gce.py
 create mode 100644 tests/data/metadata/fence_gce.xml

Index: fence-agents-4.0.22+git.1455008135.15c5e92/.travis.yml
===================================================================
--- fence-agents-4.0.22+git.1455008135.15c5e92.orig/.travis.yml
+++ fence-agents-4.0.22+git.1455008135.15c5e92/.travis.yml
@@ -16,6 +16,7 @@ before_install:
   - pip install pycurl
   - pip install requests
   - pip install pexpect
+  - pip install google-api-python-client
 
 script:
   - ./autogen.sh
Index: fence-agents-4.0.22+git.1455008135.15c5e92/configure.ac
===================================================================
--- fence-agents-4.0.22+git.1455008135.15c5e92.orig/configure.ac
+++ fence-agents-4.0.22+git.1455008135.15c5e92/configure.ac
@@ -158,6 +158,10 @@ AC_PYTHON_MODULE(pexpect, 1)
 AC_PYTHON_MODULE(pycurl, 1)
 AC_PYTHON_MODULE(requests, 1)
 
+if echo "$AGENTS_LIST" | grep -q gce; then
+	AC_PYTHON_MODULE(googleapiclient, 1)
+fi
+
 ## path to 3rd-party binaries
 AC_PATH_PROG([IPMITOOL_PATH], [ipmitool], [/usr/bin/ipmitool])
 AC_PATH_PROG([AMTTOOL_PATH], [amttool], [/usr/bin/amttool])
Index: fence-agents-4.0.22+git.1455008135.15c5e92/fence/agents/gce/fence_gce.py
===================================================================
--- /dev/null
+++ fence-agents-4.0.22+git.1455008135.15c5e92/fence/agents/gce/fence_gce.py
@@ -0,0 +1,110 @@
+#!@PYTHON@ -tt
+
+import atexit
+import sys
+sys.path.append("@FENCEAGENTSLIBDIR@")
+from googleapiclient import discovery
+from oauth2client.client import GoogleCredentials
+
+from fencing import *
+from fencing import fail, fail_usage, EC_TIMED_OUT, run_delay
+
+
+def get_nodes_list(conn, options):
+	result = {}
+	try:
+		instanceList = conn.instances().list(project=options["--project"], zone=options["--zone"]).execute()
+		for instance in instanceList["items"]:
+			status = "unknown"
+			if instance["status"] == "RUNNING":
+				status = "on"
+			elif instance["status"] == "TERMINATED":
+				status = "off"
+			result[instance["id"]] = (instance["name"], status)
+	# TODO: check which Exceptions it can throw
+	except:
+		fail_usage("Failed: Unable to connect to GCE. Check your configuration.")
+
+	return result
+
+def get_power_status(conn, options):
+	try:
+		instance = conn.instances().get(project=options["--project"], zone=options["--zone"],
+						instance=options["--plug"]).execute()
+		if instance["status"] == "RUNNING":
+			return "on"
+		elif instance["status"] == "TERMINATED":
+			return "off"
+		else:
+			return "unknown"
+	# TODO: check which Exceptions it can throw
+	except:
+		fail_usage("Failed: Unable to connect to GCE. Check your configuration.")
+
+def set_power_status(conn, options):
+	try:
+		if (options["--action"]=="off"):
+			conn.instances().stop(project=options["--project"], zone=options["--zone"],
+							instance=options["--plug"]).execute()
+		elif (options["--action"]=="on"):
+			conn.instances().start(project=options["--project"], zone=options["--zone"],
+							instance=options["--plug"]).execute()
+	# TODO: check which Exceptions it can throw
+	except :
+		fail_usage("Failed: Unable to connect to GCE. Check your configuration.")
+
+def define_new_opts():
+	all_opt["zone"] = {
+		"getopt" : ":",
+		"longopt" : "zone",
+		"help" : "--zone=[name]            Zone, e.g. us-central1-b",
+		"shortdesc" : "Zone.",
+		"required" : "0",
+		"order" : 2
+	}
+	all_opt["project"] = {
+		"getopt" : ":",
+		"longopt" : "project",
+		"help" : "--project=[name]            Project",
+		"shortdesc" : "Project.",
+		"required" : "0",
+		"order" : 3
+	}
+
+def main():
+	conn = None
+
+	device_opt = ["port", "no_password", "zone", "project"]
+
+	atexit.register(atexit_handler)
+
+	define_new_opts()
+
+	all_opt["power_timeout"]["default"] = "60"
+
+	options = check_input(device_opt, process_input(device_opt))
+
+	docs = {}
+	docs["shortdesc"] = "Fence agent for GCE (Google Cloud Engine)"
+	docs["longdesc"] = "fence_gce is an I/O Fencing agent for GCE (Google Cloud " \
+			   "Engine). It uses the googleapiclient library to connect to GCE.\n" \
+			   "googleapiclient can be configured with Google SDK CLI or by " \
+			   "executing 'gcloud auth application-default login'.\n" \
+			   "For instructions see: https://cloud.google.com/compute/docs/tutorials/python-guide"
+	docs["vendorurl"] = "http://cloud.google.com"
+	show_docs(options, docs)
+
+	run_delay(options)
+
+	try:
+		credentials = GoogleCredentials.get_application_default()
+		conn = discovery.build('compute', 'v1', credentials=credentials)
+	except:
+		fail_usage("Failed: Unable to connect to GCE. Check your configuration.")
+
+	# Operate the fencing device
+	result = fence_action(conn, options, set_power_status, get_power_status, get_nodes_list)
+	sys.exit(result)
+
+if __name__ == "__main__":
+	main()
Index: fence-agents-4.0.22+git.1455008135.15c5e92/tests/data/metadata/fence_gce.xml
===================================================================
--- /dev/null
+++ fence-agents-4.0.22+git.1455008135.15c5e92/tests/data/metadata/fence_gce.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" ?>
+<resource-agent name="fence_gce" shortdesc="Fence agent for GCE (Google Cloud Engine)" >
+<longdesc>fence_gce is an I/O Fencing agent for GCE (Google Cloud Engine). It uses the googleapiclient library to connect to GCE.
+googleapiclient can be configured with Google SDK CLI or by executing 'gcloud auth application-default login'.
+For instructions see: https://cloud.google.com/compute/docs/tutorials/python-guide</longdesc>
+<vendor-url>http://cloud.google.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="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="zone" unique="0" required="0">
+		<getopt mixed="--zone=[name]" />
+		<content type="string"  />
+		<shortdesc lang="en">Zone.</shortdesc>
+	</parameter>
+	<parameter name="project" unique="0" required="0">
+		<getopt mixed="--project=[name]" />
+		<content type="string"  />
+		<shortdesc lang="en">Project.</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</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]" />
+		<content type="string"  />
+		<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="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="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="power_timeout" unique="0" required="0">
+		<getopt mixed="--power-timeout=[seconds]" />
+		<content type="second" default="60"  />
+		<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="3"  />
+		<shortdesc lang="en">Wait X seconds for cmd prompt after issuing command</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>
+</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="validate-all" />
+</actions>
+</resource-agent>
