From ba8b29ad74d036bd908a305ad734a4390a44eb1e Mon Sep 17 00:00:00 2001
From: scalingbits <scalingbits@users.noreply.github.com>
Date: Wed, 28 Jun 2017 18:01:13 +0200
Subject: [PATCH 08/13] High: aws-vpc-route53: Add agent for AWS Route 53
 (fate#322781)

This agent changes a private zone Route 53 agent to
the appropriate IP address. This agent has been
developed in collaboration of SUSE and Amazon/AWS.
---
 heartbeat/Makefile.am     |   1 +
 heartbeat/aws-vpc-route53 | 334 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 335 insertions(+)
 create mode 100755 heartbeat/aws-vpc-route53

diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am
index 044314bf..deaa6b70 100644
--- a/heartbeat/Makefile.am
+++ b/heartbeat/Makefile.am
@@ -90,6 +90,7 @@ ocf_SCRIPTS	     =  AoEtarget		\
 			anything		\
 			apache			\
 			asterisk		\
+			aws-vpc-route53		\
 			awseip			\
 			awsvip			\
 			clvm			\
diff --git a/heartbeat/aws-vpc-route53 b/heartbeat/aws-vpc-route53
new file mode 100755
index 00000000..a6e7218a
--- /dev/null
+++ b/heartbeat/aws-vpc-route53
@@ -0,0 +1,334 @@
+#!/bin/bash
+#
+#   Copyright 2017 Amazon.com, Inc. and its affiliates. All Rights Reserved.
+#   Licensed under the MIT License.
+#
+#  Copyright 2017 Amazon.com, Inc. and its affiliates
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is furnished to do
+# so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+#
+#
+#
+# OCF resource agent to move an IP address within a VPC in the AWS
+# Written by Stefan Schneider , Martin Tegmeier (AWS)
+# Based on code of Markus Guertler#
+#
+#
+# OCF resource agent to move an IP address within a VPC in the AWS
+# Written by Stefan Schneider (AWS) , Martin Tegmeier (AWS)
+# Based on code of Markus Guertler (SUSE)
+#
+# Mar. 15, 2017, vers 1.0.2
+
+#######################################################################
+# Initialization:
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
+
+OCF_RESKEY_ttl_default=10
+
+: ${OCF_RESKEY_ttl:=${OCF_RESKEY_ttl_default}}
+
+#######################################################################
+
+usage() {
+	cat <<-EOT
+	usage: $0 {start|stop|status|monitor|validate-all|meta-data}
+	EOT
+}
+
+metadata() {
+cat <<END
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="aws-vpc-route53">
+<version>1.0</version>
+<longdesc lang="en">
+Update Route53 record of Amazon Webservices EC2 by updating an entry in a
+hosted zone ID table.
+
+AWS instances will require policies which allow them to update Route53 ARecords:
+{
+    "Version": "2012-10-17",
+    "Statement": [
+        {
+            "Sid": "Stmt1471878724000",
+            "Effect": "Allow",
+            "Action": [
+                "route53:ChangeResourceRecordSets",
+                "route53:GetChange",
+                "route53:ListResourceRecordSets",
+            ],
+            "Resource": [
+                "*"
+            ]
+        }
+    ]
+}
+
+Example Cluster Configuration:
+
+Use a configuration in "crm configure edit" which looks as follows. Replace
+hostedzoneid, fullname and profile with the appropriate values:
+
+primitive res_route53 ocf:heartbeat:aws-vpc-route53 \
+        params hostedzoneid=Z22XDORCGO4P3T fullname=suse-service5.awslab.cloud.sap.corp. profile=cluster \
+        op start interval=0 timeout=180 \
+        op stop interval=0 timeout=180 \
+        op monitor interval=300 timeout=180 \
+        meta target-role=Started
+</longdesc>
+<shortdesc lang="en">Update Route53 VPC record for AWS EC2</shortdesc>
+<parameters>
+<parameter name="hostedzoneid" required="1">
+<longdesc lang="en">
+Hosted zone ID of Route 53. This is the table of
+the Route 53 record.
+</longdesc>
+<shortdesc lang="en">AWS hosted zone ID</shortdesc>
+<content type="string" default="" />
+</parameter>
+<parameter name="fullname" required="1">
+<longdesc lang="en">
+The full name of the service which will host the IP address.
+Example: suse-service5.awslab.cloud.sap.corp.
+Note: The trailing dot is important to Route53!
+</longdesc>
+<shortdesc lang="en">Full service name</shortdesc>
+<content type="string" default="" />
+</parameter>
+<parameter name="ttl" required="0">
+<longdesc lang="en">
+Time to live for Route53 ARECORD
+</longdesc>
+<shortdesc lang="en">ARECORD TTL</shortdesc>
+<content type="string" default="${OCF_RESKEY_ttl_default}" />
+</parameter>
+<parameter name="profile" required="1">
+<longdesc lang="en">
+The name of the AWS CLI profile of the root account. This
+profile will have to use the "text" format for CLI output.
+The file /root/.aws/config should have an entry which looks
+like:
+
+  [profile cluster]
+	region = us-east-1
+	output = text
+
+"cluster" is the name which has to be used in the cluster
+configuration. The region has to be the current one. The
+output has to be "text".
+</longdesc>
+<shortdesc lang="en">AWS Profile Name</shortdesc>
+<content type="string" default="" />
+</parameter>
+</parameters>
+<actions>
+<action name="start" timeout="180" />
+<action name="stop" timeout="180" />
+<action name="monitor" depth="0" timeout="180" interval="300" />
+<action name="validate-all" timeout="5" />
+<action name="meta-data" timeout="5" />
+</actions>
+</resource-agent>
+END
+}
+
+debugger() {
+	ocf_log debug "DEBUG: $1"
+}
+
+ec2ip_validate() {
+	debugger "function: validate"
+
+	# Full name
+	[[ -z "$OCF_RESKEY_fullname" ]] && ocf_log error "Full name parameter not set $OCF_RESKEY_fullname!" && exit $OCF_ERR_CONFIGURED
+
+	# Hosted Zone ID
+	[[ -z "$OCF_RESKEY_hostedzoneid" ]] && ocf_log error "Hosted Zone ID parameter not set $OCF_RESKEY_hostedzoneid!" && exit $OCF_ERR_CONFIGURED
+
+	# profile
+	[[ -z "$OCF_RESKEY_profile" ]] && ocf_log error "AWS CLI profile not set $OCF_RESKEY_profile!" && exit $OCF_ERR_CONFIGURED
+
+	# TTL
+	[[ -z "$OCF_RESKEY_ttl" ]] && ocf_log error "TTL not set $OCF_RESKEY_ttl!" && exit $OCF_ERR_CONFIGURED
+
+	debugger "Testing aws command"
+	aws --version 2>&1
+	if [ "$?" -gt 0 ]; then
+		error "Error while executing aws command as user root! Please check if AWS CLI tools (Python flavor) are properly installed and configured." && exit $OCF_ERR_INSTALLED
+	fi
+	debugger "ok"
+
+	if [ -n "$OCF_RESKEY_profile" ]; then
+		AWS_PROFILE_OPT="--profile $OCF_RESKEY_profile"
+	else
+		AWS_PROFILE_OPT="--profile default"
+	fi
+
+	return $OCF_SUCCESS
+}
+
+ec2ip_monitor() {
+	ec2ip_validate
+	debugger "function: ec2ip_monitor: check Route53 record "
+	IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')"
+	ARECORD="$(aws $AWS_PROFILE_OPT route53 list-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --query "ResourceRecordSets[?Name=='$OCF_RESKEY_fullname']" | grep RESOURCERECORDS | /usr/bin/awk '{ print $2 }' )"
+	debugger "function: ec2ip_monitor: found IP address: $ARECORD ."
+	if [ "${ARECORD}" == "${IPADDRESS}" ]; then
+		debugger "function: ec2ip_monitor:  ARECORD $ARECORD found"
+		return $OCF_SUCCESS
+	else
+		debugger "function: ec2ip_monitor: no ARECORD found"
+		return $OCF_NOT_RUNNING
+	fi
+
+	return $OCF_SUCCESS
+}
+
+ec2ip_stop() {
+	ocf_log info "EC2: Bringing down Route53 agent. (Will remove ARECORD)"
+	IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')"
+	ARECORD="$(aws $AWS_PROFILE_OPT route53 list-resource-record-sets --hosted-zone-id $OCF_RESKEY_hostedzoneid --query "ResourceRecordSets[?Name=='$OCF_RESKEY_fullname']" | grep RESOURCERECORDS | /usr/bin/awk '{ print $2 }' )"
+	debugger "function: ec2ip_monitor: found IP address: $ARECORD ."
+	if [ ${ARECORD} != ${IPADDRESS} ]; then
+		debugger "function: ec2ip_monitor: no ARECORD found"
+		return $OCF_SUCCESS
+	else
+		debugger "function: ec2ip_monitor:	ARECORD $ARECORD found"
+		# determine IP address
+		IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')"
+		# Patch file
+		debugger "function ec2ip_stop: will delete IP address to ${IPADDRESS}"
+		ocf_log info "EC2: Updating Route53 $OCF_RESKEY_hostedzoneid with $IPADDRESS for $OCF_RESKEY_fullname"
+		ROUTE53RECORD="/var/tmp/route53-${OCF_RESKEY_hostedzoneid}-${IPADDRESS}.json"
+		echo "{ " > ${ROUTE53RECORD}
+		echo "	  \"Comment\": \"Update record to reflect new IP address for a system \", " >>	${ROUTE53RECORD}
+		echo "	  \"Changes\": [ " >>  ${ROUTE53RECORD}
+		echo "		  { " >>  ${ROUTE53RECORD}
+		echo "			  \"Action\": \"DELETE\", " >>	${ROUTE53RECORD}
+		echo "			  \"ResourceRecordSet\": { " >>	 ${ROUTE53RECORD}
+		echo "				  \"Name\": \"${OCF_RESKEY_fullname}\", " >>  ${ROUTE53RECORD}
+		echo "				  \"Type\": \"A\", " >>	 ${ROUTE53RECORD}
+		echo "				  \"TTL\": ${OCF_RESKEY_ttl}, " >>	${ROUTE53RECORD}
+		echo "				  \"ResourceRecords\": [ " >>  ${ROUTE53RECORD}
+		echo "					  { " >>  ${ROUTE53RECORD}
+		echo "						  \"Value\": \"${IPADDRESS}\" " >>	${ROUTE53RECORD}
+		echo "					  } " >>  ${ROUTE53RECORD}
+		echo "				  ] " >>  ${ROUTE53RECORD}
+		echo "			  } " >>  ${ROUTE53RECORD}
+		echo "		  } " >>  ${ROUTE53RECORD}
+		echo "	  ] " >>  ${ROUTE53RECORD}
+		echo "}" >> ${ROUTE53RECORD}
+		cmd="aws --profile ${OCF_RESKEY_profile} route53 change-resource-record-sets --hosted-zone-id ${OCF_RESKEY_hostedzoneid} \
+		  --change-batch file://${ROUTE53RECORD} "
+		debugger "function ec2ip_start: executing command: $cmd"
+		CHANGEID=$($cmd | grep CHANGEINFO |	 /usr/bin/awk -F'\t' '{ print $3 }' )
+		debugger "Change id: ${CHANGEID}"
+		rm ${ROUTE53RECORD}
+		CHANGEID=$(echo $CHANGEID |cut -d'/' -f 3 |cut -d'"' -f 1 )
+		debugger "Change id: ${CHANGEID}"
+		STATUS="PENDING"
+		MYSECONDS=2
+		while [ "$STATUS" = 'PENDING' ]; do
+			sleep	${MYSECONDS}
+			STATUS="$(aws --profile ${OCF_RESKEY_profile} route53 get-change --id $CHANGEID | grep CHANGEINFO |  /usr/bin/awk -F'\t' '{ print $4 }' |cut -d'"' -f 2 )"
+			debugger "Waited for ${MYSECONDS} seconds and checked execution of Route 53 update status: ${STATUS} "
+		done
+
+		return $OCF_SUCCESS
+	fi
+
+	return $OCF_SUCCESS
+}
+
+ec2ip_start() {
+	# determine IP address
+	IPADDRESS="$(ec2metadata aws ip | grep local-ipv4 | /usr/bin/awk '{ print $2 }')"
+	# Patch file
+	debugger "function ec2ip_start: will update IP address to ${IPADDRESS}"
+	ocf_log info "EC2: Updating Route53 $OCF_RESKEY_hostedzoneid with $IPADDRESS for $OCF_RESKEY_fullname"
+	ROUTE53RECORD="/var/tmp/route53-${OCF_RESKEY_hostedzoneid}-${IPADDRESS}.json"
+	echo "{ " > ${ROUTE53RECORD}
+	echo "    \"Comment\": \"Update record to reflect new IP address for a system \", " >>  ${ROUTE53RECORD}
+	echo "    \"Changes\": [ " >>  ${ROUTE53RECORD}
+	echo "        { " >>  ${ROUTE53RECORD}
+	echo "            \"Action\": \"UPSERT\", " >>  ${ROUTE53RECORD}
+	echo "            \"ResourceRecordSet\": { " >>  ${ROUTE53RECORD}
+	echo "                \"Name\": \"${OCF_RESKEY_fullname}\", " >>  ${ROUTE53RECORD}
+	echo "                \"Type\": \"A\", " >>  ${ROUTE53RECORD}
+	echo "                \"TTL\": ${OCF_RESKEY_ttl} , " >>  ${ROUTE53RECORD}
+	echo "                \"ResourceRecords\": [ " >>  ${ROUTE53RECORD}
+	echo "                    { " >>  ${ROUTE53RECORD}
+	echo "                        \"Value\": \"${IPADDRESS}\" " >>  ${ROUTE53RECORD}
+	echo "                    } " >>  ${ROUTE53RECORD}
+	echo "                ] " >>  ${ROUTE53RECORD}
+	echo "            } " >>  ${ROUTE53RECORD}
+	echo "        } " >>  ${ROUTE53RECORD}
+	echo "    ] " >>  ${ROUTE53RECORD}
+	echo "}" >> ${ROUTE53RECORD}
+	cmd="aws --profile ${OCF_RESKEY_profile} route53 change-resource-record-sets --hosted-zone-id ${OCF_RESKEY_hostedzoneid} \
+	  --change-batch file://${ROUTE53RECORD} "
+	debugger "function ec2ip_start: executing command: $cmd"
+	CHANGEID=$($cmd | grep CHANGEINFO |  /usr/bin/awk -F'\t' '{ print $3 }' )
+	debugger "Change id: ${CHANGEID}"
+	rm ${ROUTE53RECORD}
+	CHANGEID=$(echo $CHANGEID |cut -d'/' -f 3 |cut -d'"' -f 1 )
+	debugger "Change id: ${CHANGEID}"
+	STATUS="PENDING"
+	MYSECONDS=2
+	while [ "$STATUS" = 'PENDING' ]; do
+		sleep  ${MYSECONDS}
+		STATUS="$(aws --profile ${OCF_RESKEY_profile} route53 get-change --id $CHANGEID | grep CHANGEINFO |  /usr/bin/awk -F'\t' '{ print $4 }' |cut -d'"' -f 2 )"
+		debugger "Waited for ${MYSECONDS} seconds and checked execution of Route 53 update status: ${STATUS} "
+	done
+
+	return $OCF_SUCCESS
+}
+
+###############################################################################
+
+case $__OCF_ACTION in
+	usage|help)
+		usage
+		exit $OCF_SUCCESS
+		;;
+	meta-data)
+		metadata
+		exit $OCF_SUCCESS
+		;;
+	monitor)
+		ec2ip_monitor
+		;;
+	stop)
+		ec2ip_stop
+		;;
+	validate-all)
+		ec2ip_validate
+		;;
+	start)
+		ec2ip_start
+		;;
+	*)
+		usage
+		exit $OCF_ERR_UNIMPLEMENTED
+		;;
+esac
-- 
2.14.1

