diff options
Diffstat (limited to '')
-rwxr-xr-x | heartbeat/dnsupdate.in | 381 |
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 $? + |