#!/bin/bash
#
#
# Support:      users@clusterlabs.org
# License:      GNU General Public License v2
#
# Copyright (c) 2014 SUSE Linux Products GmbH, Lars Marowsky-Brée
#                    All Rights Reserved.
#
#######################################################################
: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
#######################################################################

# TODO:
# - Should setting CNAMEs be supported?
# - Should multiple A records be supported?

usage() {
  cat <<-!
	usage: $0 {start|stop|status|monitor|meta-data|validate-all}
	!
}

meta_data() {
	cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="dnsupdate">
<version>1.0</version>

<longdesc lang="en">
This resource agent manages IP take-over via dynamic DNS updates.
</longdesc>
<shortdesc lang="en">IP take-over via dynamic DNS update</shortdesc>

<parameters>

<parameter name="hostname" unique="1" required="1">
<longdesc lang="en">
The hostname whose IP address will need to be updated.
</longdesc>
<shortdesc lang="en">Hostname to update</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="ip" unique="0" required="1">
<longdesc lang="en">
IP address to set.
</longdesc>
<shortdesc lang="en">IP address to set</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="ttl" unique="0" required="0">
<longdesc lang="en">
Time to live, in seconds, for the DNS record. This
affects how soon DNS updates propagate. It should be
a reasonable compromise between update speed and DNS
server load.

If using booth, the ticket timeout is a good start.
</longdesc>
<shortdesc lang="en">TTL for the DNS record</shortdesc>
<content type="integer" default="300" />
</parameter>

<parameter name="keyfile" unique="0" required="0">
<longdesc lang="en">
The file containing the shared secret needed to update
the DNS record. Please see the nsupdate man page for
the exact syntax.
</longdesc>
<shortdesc lang="en">nsupdate key file</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="server" unique="0" required="0">
<longdesc lang="en">
Which DNS server to send these updates for. When no
server is provided, this defaults to the master server
for the correct zone.
</longdesc>
<shortdesc lang="en">DNS server to contact</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="serverport" unique="0" required="0">
<longdesc lang="en">
Port number on the DNS server.

Note: due to a limitation in the nsupdate command, this option will only
take effect if you also specify the DNS server!
</longdesc>
<shortdesc lang="en">Port number on the DNS server</shortdesc>
<content type="integer" default="53" />
</parameter>

<parameter name="nsupdate_opts" unique="0" required="0">
<longdesc lang="en">
Additional options to be passed to nsupdate.
</longdesc>
<shortdesc lang="en">Additional nsupdate options</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="unregister_on_stop" unique="0" required="0">
<longdesc lang="en">
Whether or not to actively remove records on stop. This is not needed
for normal operation, since the site taking over the IP address will
delete all previous records.
</longdesc>
<shortdesc lang="en">Remove A record on stop</shortdesc>
<content type="boolean" default="false" />
</parameter>

</parameters>

<actions>
<action name="start" timeout="30" />
<action name="stop" timeout="30" />
<action name="status" depth="0" timeout="30" interval="10" />
<action name="monitor" depth="0" timeout="30" interval="10" />
<action name="meta-data" timeout="5" />
<action name="validate-all" timeout="5" />
</actions>
</resource-agent>
END
}

dnsupdate_status() {
	# The resource is considered active if the current IP
	# address is returned as the only response.
	local record=$(dig ${dig_opts} ${hostname}. A +short 2>/dev/null)
	if [ "$record" = "$ip" ]; then
		return $OCF_SUCCESS
	fi
	return $OCF_NOT_RUNNING
}

dnsupdate_monitor() {
	if ocf_is_probe ; then
		# 
		return $OCF_NOT_RUNNING
	fi
	dnsupdate_status
}

dnsupdate_start() {
	if dnsupdate_status ; then
		ocf_log info "$hostname already resolves to $ip"
		return $OCF_SUCCESS
	fi

	ocf_log info "Updating DNS records for $hostname"

	(
	if [ -n "$dns_server" ]; then
		echo "server ${dns_server} ${dns_serverport}"
	fi
	echo "update delete $hostname A"
	echo "update add $hostname ${OCF_RESKEY_ttl} A $ip"
	echo "send"
	) | nsupdate ${nsupdate_opts}

	dnsupdate_monitor

	return $?
}

dnsupdate_stop() {
	if ocf_is_true "${OCF_RESKEY_unregister_on_stop}" && dnsupdate_status ; then
		ocf_log info "Unregistering $hostname with $ip from DNS server"
		(
		if [ -n "$dns_server" ]; then
			echo "server ${dns_server} ${dns_serverport}"
		fi
		echo "update delete $hostname A $ip"
		echo "send"
		) | nsupdate ${nsupdate_opts}

		dnsupdate_monitor
		if [ $? -ne $OCF_NOT_RUNNING ]; then
			ocf_log warn "Unregistering failed!"
			# There's no point in invoking a stop failure
			# here. If another site takes over the record,
			# it'll delete all previous entries anyway.
		fi
	fi
	return $OCF_SUCCESS
}

dnsupdate_validate() {
	hostname=${OCF_RESKEY_hostname}
	ip=${OCF_RESKEY_ip}
	dig_opts=""
	dns_server=${OCF_RESKEY_server}
	: ${OCF_RESKEY_serverport:="53"}
	dns_serverport=${OCF_RESKEY_serverport}
	: ${OCF_RESKEY_ttl:="300"}
	nsupdate_opts=${OCF_RESKEY_nsupdate_opts}
	if [ -z "$nsupdate_opts" -a -n "$OCF_RESKEY_opts" ]; then
		nsupdate_opts=${OCF_RESKEY_opts}
		ocf_log warn "opts was never an advertised parameter, please use nsupdate_opts"
	fi

	if [ -z "$hostname" ]; then
		ocf_log err "No hostname specified."
		exit $OCF_ERR_CONFIGURED
	fi
	if [ -z "$ip" ]; then
		ocf_log err "No IP specified."
		exit $OCF_ERR_CONFIGURED
	fi
	if ! ocf_is_decimal $OCF_RESKEY_ttl ; then
		ocf_log err "ttl $OCF_RESKEY_ttl is not valid"
		exit $OCF_ERR_CONFIGURED
	fi

	if ! ocf_is_decimal $dns_serverport ; then
		ocf_log err "serverport $dns_serverport is not valid"
		exit $OCF_ERR_CONFIGURED
	fi
	dig_opts+=" -p ${dns_serverport}"

	if [ -n "$dns_server" ]; then
		dig_opts+=" @${dns_server}"
	fi

	if [ -n "$OCF_RESKEY_keyfile" ]; then
		if [ ! -f ${OCF_RESKEY_keyfile} ]; then
			ocf_log err "keyfile $OCF_RESKEY_keyfile does not exist"
			exit $OCF_ERR_CONFIGURED
		fi
		nsupdate_opts+=" -k $OCF_RESKEY_keyfile"
	fi
}

if [ $# -ne 1 ]; then
  usage
  exit $OCF_ERR_ARGS
fi

case $1 in
meta-data)	meta_data
		exit $OCF_SUCCESS
		;;
usage)		usage
		exit $OCF_SUCCESS
		;;
esac

check_binary dig
check_binary nsupdate

dnsupdate_validate

case $1 in
start)		dnsupdate_start
		;;
stop)		dnsupdate_stop
		;;
monitor)	dnsupdate_monitor
		;;
status)		dnsupdate_status
		;;
validate-all)	# We've already run this
		exit $OCF_SUCCESS
		;;
*)		usage
		exit $OCF_ERR_UNIMPLEMENTED
		;;
esac
exit $?


