summaryrefslogtreecommitdiffstats
path: root/heartbeat/dnsupdate.in
diff options
context:
space:
mode:
Diffstat (limited to 'heartbeat/dnsupdate.in')
-rwxr-xr-xheartbeat/dnsupdate.in381
1 files changed, 381 insertions, 0 deletions
diff --git a/heartbeat/dnsupdate.in b/heartbeat/dnsupdate.in
new file mode 100755
index 0000000..b54822c
--- /dev/null
+++ b/heartbeat/dnsupdate.in
@@ -0,0 +1,381 @@
+#!@BASH_SHELL@
+#
+#
+# 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
+
+# Parameter defaults
+
+OCF_RESKEY_hostname_default=""
+OCF_RESKEY_type_default="A"
+OCF_RESKEY_ip_default=""
+OCF_RESKEY_cname_default=""
+OCF_RESKEY_ttl_default="300"
+OCF_RESKEY_keyfile_default=""
+OCF_RESKEY_server_default=""
+OCF_RESKEY_serverport_default="53"
+OCF_RESKEY_nsupdate_opts_default=""
+OCF_RESKEY_unregister_on_stop_default="false"
+
+: ${OCF_RESKEY_hostname=${OCF_RESKEY_hostname_default}}
+: ${OCF_RESKEY_cname=${OCF_RESKEY_cname_default}}
+: ${OCF_RESKEY_type=${OCF_RESKEY_type_default}}
+: ${OCF_RESKEY_ip=${OCF_RESKEY_ip_default}}
+: ${OCF_RESKEY_ttl=${OCF_RESKEY_ttl_default}}
+: ${OCF_RESKEY_keyfile=${OCF_RESKEY_keyfile_default}}
+: ${OCF_RESKEY_server=${OCF_RESKEY_server_default}}
+: ${OCF_RESKEY_serverport=${OCF_RESKEY_serverport_default}}
+: ${OCF_RESKEY_nsupdate_opts=${OCF_RESKEY_nsupdate_opts_default}}
+: ${OCF_RESKEY_unregister_on_stop=${OCF_RESKEY_unregister_on_stop_default}}
+
+#######################################################################
+
+# TODO:
+# - 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>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">
+Either the hostname whose IP address will need to be updated (in case of type=A)
+or alias whose hostname will need to be updated (in case of type=CNAME).
+</longdesc>
+<shortdesc lang="en">Hostname to update</shortdesc>
+<content type="string" default="${OCF_RESKEY_hostname_default}" />
+</parameter>
+
+<parameter name="type" unique="0" required="0">
+<longdesc lang="en">
+The type of DNS record that need to be updated (A or CNAME).
+</longdesc>
+<shortdesc lang="en">Type of DNS record</shortdesc>
+<content type="string" default="${OCF_RESKEY_type_default}" />
+</parameter>
+
+<parameter name="ip" unique="0" required="0">
+<longdesc lang="en">
+IP address to set.
+</longdesc>
+<shortdesc lang="en">IP address to set</shortdesc>
+<content type="string" default="${OCF_RESKEY_ip_default}" />
+</parameter>
+
+<parameter name="cname" unique="0" required="0">
+<longdesc lang="en">
+The CNAME whose hostname address will need to be updated.
+</longdesc>
+<shortdesc lang="en">CNAME to update</shortdesc>
+<content type="string" default="${OCF_RESKEY_cname_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="${OCF_RESKEY_ttl_default}" />
+</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="${OCF_RESKEY_keyfile_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 promoted server
+for the correct zone.
+</longdesc>
+<shortdesc lang="en">DNS server to contact</shortdesc>
+<content type="string" default="${OCF_RESKEY_server_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="${OCF_RESKEY_serverport_default}" />
+</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="${OCF_RESKEY_nsupdate_opts_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="${OCF_RESKEY_unregister_on_stop_default}" />
+</parameter>
+
+</parameters>
+
+<actions>
+<action name="start" timeout="30s" />
+<action name="stop" timeout="30s" />
+<action name="status" depth="0" timeout="30s" interval="10s" />
+<action name="monitor" depth="0" timeout="30s" interval="10s" />
+<action name="meta-data" timeout="5s" />
+<action name="validate-all" timeout="5s" />
+</actions>
+</resource-agent>
+END
+}
+
+dnsupdate_status() {
+ case $type in
+ A)
+ # 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
+ ;;
+ CNAME)
+ local record=$(dig ${dig_opts} ${cname}. CNAME +short 2>/dev/null)
+ if [ "$record" = "${hostname}." ]; then
+ return $OCF_SUCCESS
+ fi
+ return $OCF_NOT_RUNNING
+ ;;
+ esac
+
+}
+
+dnsupdate_monitor() {
+ if ocf_is_probe ; then
+ #
+ return $OCF_NOT_RUNNING
+ fi
+ dnsupdate_status
+}
+
+dnsupdate_start() {
+ case $type in
+ A)
+ 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}
+ ;;
+
+ CNAME)
+ if dnsupdate_status ; then
+ ocf_log info "$cname already is an alias to $hostname"
+ return $OCF_SUCCESS
+ fi
+
+ ocf_log info "Updating DNS records for $cname"
+
+ (
+ if [ -n "$dns_server" ]; then
+ echo "server ${dns_server} ${dns_serverport}"
+ fi
+ echo "update delete $cname CNAME"
+ echo "update add $cname ${OCF_RESKEY_ttl} CNAME $hostname"
+ echo "send"
+ ) | nsupdate ${nsupdate_opts}
+ ;;
+
+ esac
+
+
+ dnsupdate_monitor
+
+ return $?
+}
+
+dnsupdate_stop() {
+ case $type in
+ A)
+ 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!"
+ fi
+ fi
+ return $OCF_SUCCESS
+ ;;
+ CNAME)
+ if ocf_is_true "${OCF_RESKEY_unregister_on_stop}" && dnsupdate_status ; then
+ ocf_log info "Unregistering $cname with $hostname from DNS server"
+ (
+ if [ -n "$dns_server" ]; then
+ echo "server ${dns_server} ${dns_serverport}"
+ fi
+ echo "update delete $cname CNAME"
+ echo "send"
+ ) | nsupdate ${nsupdate_opts}
+
+ dnsupdate_monitor
+ if [ $? -ne $OCF_NOT_RUNNING ]; then
+ ocf_log warn "Unregistering failed!"
+ fi
+ fi
+ return $OCF_SUCCESS
+ ;;
+ esac
+}
+
+dnsupdate_validate() {
+ hostname=${OCF_RESKEY_hostname}
+ ip=${OCF_RESKEY_ip}
+ #added support for CNAME
+ type=${OCF_RESKEY_type}
+ cname=${OCF_RESKEY_cname}
+ #
+ 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" ] && [ "$type" = "A" ]; then
+ ocf_log err "No IP specified."
+ exit $OCF_ERR_CONFIGURED
+ fi
+ #added support for CNAME
+ if [ -z "$type" ]; then
+ ocf_log err "No TYPE 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 $?
+