#!/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
