1#!/usr/local/bin/bash
2#
3# Description:  Manages a named (Bind) server as an OCF High-Availability
4#               resource
5#
6# Authors:      Serge Dubrouski (sergeyfd@gmail.com)
7#
8# Copyright:    2011 Serge Dubrouski <sergeyfd@gmail.com>
9#
10# License:      GNU General Public License (GPL)
11#
12###############################################################################
13# Initialization:
14
15: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
16. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
17
18#Defaults
19OCF_RESKEY_named_default="/usr/sbin/named"
20OCF_RESKEY_rndc_default="/usr/sbin/rndc"
21OCF_RESKEY_host_default="/usr/bin/host"
22OCF_RESKEY_named_user_default=named
23OCF_RESKEY_named_config_default=""
24OCF_RESKEY_named_pidfile_default="/var/run/named/named.pid"
25OCF_RESKEY_named_rootdir_default=""
26OCF_RESKEY_named_options_default=""
27OCF_RESKEY_named_keytab_file_default=""
28OCF_RESKEY_rndc_options_default=""
29OCF_RESKEY_host_options_default=""
30OCF_RESKEY_monitor_request_default="localhost"
31OCF_RESKEY_monitor_response_default="127.0.0.1"
32OCF_RESKEY_monitor_ip_default="127.0.0.1"
33
34: ${OCF_RESKEY_named=${OCF_RESKEY_named_default}}
35: ${OCF_RESKEY_rndc=${OCF_RESKEY_rndc_default}}
36: ${OCF_RESKEY_host=${OCF_RESKEY_host_default}}
37: ${OCF_RESKEY_named_user=${OCF_RESKEY_named_user_default}}
38: ${OCF_RESKEY_named_config=${OCF_RESKEY_named_config_default}}
39: ${OCF_RESKEY_named_pidfile=${OCF_RESKEY_named_pidfile_default}}
40: ${OCF_RESKEY_named_rootdir=${OCF_RESKEY_named_rootdir_default}}
41: ${OCF_RESKEY_named_options=${OCF_RESKEY_named_options_default}}
42: ${OCF_RESKEY_named_keytab_file=${OCF_RESKEY_named_keytab_file_default}}
43: ${OCF_RESKEY_rndc_options=${OCF_RESKEY_rndc_options_default}}
44: ${OCF_RESKEY_host_options=${OCF_RESKEY_host_options_default}}
45: ${OCF_RESKEY_monitor_request=${OCF_RESKEY_monitor_request_default}}
46: ${OCF_RESKEY_monitor_response=${OCF_RESKEY_monitor_response_default}}
47: ${OCF_RESKEY_monitor_ip=${OCF_RESKEY_monitor_ip_default}}
48
49usage() {
50    cat <<EOF
51        usage: $0 start|stop|reload|status|monitor|meta-data|validate-all|methods
52
53        $0 manages named (Bind) server as an HA resource.
54
55        The 'start' operation starts named server.
56        The 'stop' operation stops  named server.
57        The 'reload' operation reload named configuration.
58        The 'status' operation reports whether named is up.
59        The 'monitor' operation reports whether named is running.
60        The 'validate-all' operation reports whether parameters are valid.
61        The 'methods' operation reports on the methods $0 supports.
62EOF
63  return $OCF_ERR_ARGS
64}
65
66named_meta_data() {
67        cat <<EOF
68<?xml version="1.0"?>
69<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
70<resource-agent name="named">
71<version>1.0</version>
72
73<longdesc lang="en">
74Resource script for named (Bind) server. It manages named as an HA resource.
75</longdesc>
76<shortdesc lang="en">Manages a named server</shortdesc>
77
78<parameters>
79<parameter name="named" unique="0" required="0">
80<longdesc lang="en">
81Path to the named command.
82</longdesc>
83<shortdesc lang="en">named</shortdesc>
84<content type="string" default="${OCF_RESKEY_named_default}" />
85</parameter>
86
87<parameter name="rndc" unique="0" required="0">
88<longdesc lang="en">
89Path to the rndc command.
90</longdesc>
91<shortdesc lang="en">rndc</shortdesc>
92<content type="string" default="${OCF_RESKEY_rndc_default}" />
93</parameter>
94
95<parameter name="host" unique="0" required="0">
96<longdesc lang="en">
97Path to the host command.
98</longdesc>
99<shortdesc lang="en">host</shortdesc>
100<content type="string" default="${OCF_RESKEY_host_default}" />
101</parameter>
102
103<parameter name="named_user" unique="0" required="0">
104<longdesc lang="en">
105User that should own named process.
106</longdesc>
107<shortdesc lang="en">named_user</shortdesc>
108<content type="string" default="${OCF_RESKEY_named_user_default}" />
109</parameter>
110
111<parameter name="named_config" unique="1" required="0">
112<longdesc lang="en">
113Configuration file for named.
114</longdesc>
115<shortdesc lang="en">named_config</shortdesc>
116<content type="string" default="${OCF_RESKEY_named_config_default}" />
117</parameter>
118
119<parameter name="named_pidfile" unique="1" required="0">
120<longdesc lang="en">
121PIDFILE file for named.
122</longdesc>
123<shortdesc lang="en">named_pidfile</shortdesc>
124<content type="string" default="${OCF_RESKEY_named_pidfile_default}" />
125</parameter>
126
127<parameter name="named_rootdir" unique="1" required="0">
128<longdesc lang="en">
129Directory that named should use for chroot if any.
130</longdesc>
131<shortdesc lang="en">named_rootdir</shortdesc>
132<content type="string" default="${OCF_RESKEY_named_rootdir_default}" />
133</parameter>
134
135<parameter name="named_options" unique="0" required="0">
136<longdesc lang="en">
137Options for named process if any.
138</longdesc>
139<shortdesc lang="en">named_options</shortdesc>
140<content type="string" default="${OCF_RESKEY_named_options_default}" />
141</parameter>
142
143<parameter name="named_keytab_file" unique="0" required="0">
144<longdesc lang="en">
145named service keytab file (for GSS-TSIG).
146</longdesc>
147<shortdesc lang="en">named_keytab_file</shortdesc>
148<content type="string" default="${OCF_RESKEY_named_keytab_file_default}" />
149</parameter>
150
151<parameter name="rndc_options" unique="0" required="0">
152<longdesc lang="en">
153Options for rndc process if any.
154</longdesc>
155<shortdesc lang="en">rndc_options</shortdesc>
156<content type="string" default="${OCF_RESKEY_rndc_options_default}" />
157</parameter>
158
159<parameter name="host_options" unique="0" required="0">
160<longdesc lang="en">
161Options for host process if any.
162</longdesc>
163<shortdesc lang="en">host_options</shortdesc>
164<content type="string" default="${OCF_RESKEY_host_options_default}" />
165</parameter>
166
167<parameter name="monitor_request" unique="0" required="0">
168<longdesc lang="en">
169Request that shall be sent to named for monitoring. Usually an A record in DNS.
170</longdesc>
171<shortdesc lang="en">monitor_request</shortdesc>
172<content type="string" default="${OCF_RESKEY_monitor_request_default}" />
173</parameter>
174
175<parameter name="monitor_response" unique="0" required="0">
176<longdesc lang="en">
177Expected response from named server.
178</longdesc>
179<shortdesc lang="en">monitor_response</shortdesc>
180<content type="string" default="${OCF_RESKEY_monitor_response_default}" />
181</parameter>
182
183<parameter name="monitor_ip" unique="0" required="0">
184<longdesc lang="en">
185IP Address where named listens.
186</longdesc>
187<shortdesc lang="en">monitor_ip</shortdesc>
188<content type="string" default="${OCF_RESKEY_monitor_ip_default}" />
189</parameter>
190</parameters>
191
192<actions>
193<action name="start" timeout="60s" />
194<action name="stop" timeout="60s" />
195<action name="reload" timeout="60s" />
196<action name="status" timeout="10s" />
197<action name="monitor" depth="0" timeout="30s" interval="30s"/>
198<action name="meta-data" timeout="5s" />
199<action name="validate-all" timeout="5s" />
200<action name="methods" timeout="5s" />
201</actions>
202</resource-agent>
203
204EOF
205}
206
207#
208# methods: What methods/operations do we support?
209#
210
211named_methods() {
212  cat <<EOF
213        start
214        stop
215        status
216        monitor
217        methods 
218        meta-data
219        validate-all
220EOF
221}
222
223# Validate most critical parameters
224named_validate_all() {
225    check_binary $OCF_RESKEY_named
226    check_binary $OCF_RESKEY_rndc
227    check_binary $OCF_RESKEY_host
228
229    if [ -n "$OCF_RESKEY_named_config" -a \
230        ! -r "${OCF_RESKEY_named_rootdir}/${OCF_RESKEY_named_config}" ]; then
231        if ocf_is_probe; then
232           ocf_log info "Configuration file ${OCF_RESKEY_named_rootdir}/${OCF_RESKEY_named_config} not readable during probe."
233        else
234           ocf_exit_reason "Configuration file ${OCF_RESKEY_named_rootdir}/${OCF_RESKEY_named_config} doesn't exist"
235           return $OCF_ERR_INSTALLED
236        fi
237    fi
238
239    getent passwd $OCF_RESKEY_named_user >/dev/null 2>&1
240    if [ ! $? -eq 0 ]; then
241        ocf_exit_reason "User $OCF_RESKEY_named_user doesn't exist";
242        return $OCF_ERR_INSTALLED;
243    fi
244
245    if [ -z "$OCF_RESKEY_monitor_request" -o \
246         -z "$OCF_RESKEY_monitor_response" -o \
247         -z "$OCF_RESKEY_monitor_ip" ]; then
248        ocf_exit_reason "None of monitor_request, monitor_response, and monitor_ip can be empty"
249        return $OCF_ERR_CONFIGURED
250    fi
251
252    # make sure that the pidfile directory exists
253    ocf_mkstatedir $OCF_RESKEY_named_user 755 `dirname $OCF_RESKEY_named_pidfile` || return $OCF_ERR_INSTALLED
254
255    return $OCF_SUCCESS
256}
257
258##
259# Attempt to generate a /etc/rndc.key if one is not present
260##
261rndc_key_generator()
262{
263    local rndc_options="-a -r /dev/urandom -u $OCF_RESKEY_named_user"
264
265    if [ -s /etc/rndc.key ]; then
266        # file already exists
267        return
268    fi
269
270    if ! have_binary "rndc-confgen"; then
271        # can't autogen key... Report this, but not as a warning or error.
272        # It is possible that the user configured the key in named.conf
273        ocf_log info "rndc-confgen tool not present, unable to autogen /etc/rndc.key."
274        return
275    fi
276
277    if [ -n "$OCF_RESKEY_rootdir" ]; then
278        rndc_options="$rndc_options -t $OCF_RESKEY_rootdir"
279    fi
280
281    rndc-confgen $rndc_options > /dev/null 2>&1;
282    if [ $? -eq 0 ]; then
283        if have_binary "restorecon"; then
284            restorecon /etc/rndc.key
285        fi
286    else
287        ocf_log info "failed to auto-generate /etc/rndc.key file."
288    fi
289}
290
291#
292# named_getpid. Get pid of named process with a given parameters.
293#
294
295named_getpid () {
296    local pattern="$OCF_RESKEY_named"
297
298    if [ -n "$OCF_RESKEY_named_rootdir" -a "x${OCF_RESKEY_named_rootdir}" != "x/" ]; then
299       pattern="$pattern.*-t $OCF_RESKEY_named_rootdir"
300    fi
301
302    if [ -n "$OCF_RESKEY_named_config" ]; then
303       pattern="$pattern.*-c $OCF_RESKEY_named_config"
304    fi
305
306    pid=`pgrep -f "$pattern"`
307    echo $pid
308}
309
310#
311# named_status. Simple check of the status of named process by pidfile.
312#
313
314named_status () {
315    ocf_pidfile_status ${OCF_RESKEY_named_pidfile} >/dev/null 2>&1
316}
317
318#
319# named_monitor. Send a request to named and check response.
320#
321
322named_monitor() {
323    local output
324
325    if ! named_status
326    then
327        ocf_log info "named is down"
328        return $OCF_NOT_RUNNING
329    fi
330
331    output=`$OCF_RESKEY_host $OCF_RESKEY_host_options $OCF_RESKEY_monitor_request $OCF_RESKEY_monitor_ip`
332
333    if [ $? -ne 0 ] || ! echo $output | grep -q '.* has .*address '"$OCF_RESKEY_monitor_response"
334    then
335       ocf_exit_reason "named didn't answer properly for $OCF_RESKEY_monitor_request."
336       ocf_log err "Expected: $OCF_RESKEY_monitor_response."
337       ocf_log err "Got: $output"
338       return $OCF_ERR_GENERIC
339    fi
340
341    return $OCF_SUCCESS
342}
343
344#
345# Reload
346#
347
348named_reload() {
349    $OCF_RESKEY_rndc $OCF_RESKEY_rndc_options reload >/dev/null || return $OCF_ERR_GENERIC
350
351    return $OCF_SUCCESS
352}
353
354#
355# Start
356#
357
358named_start() {
359    local root_dir_opt
360    local pid
361
362    root_dir_opt=""
363    named_status && return $OCF_SUCCESS
364
365    # Remove pidfile if exists
366    rm -f ${OCF_RESKEY_named_pidfile}
367
368    if [ -n "${OCF_RESKEY_named_rootdir}" -a "x${OCF_RESKEY_named_rootdir}" != "x/" ]
369    then
370        root_dir_opt="-t ${OCF_RESKEY_named_rootdir}"
371        [ -s /etc/localtime ] && cp -fp /etc/localtime ${OCF_RESKEY_named_rootdir}/etc/localtime
372    fi
373
374    if [ -n "$OCF_RESKEY_named_config" ]; then
375        OCF_RESKEY_named_options="-c $OCF_RESKEY_named_config $OCF_RESKEY_named_options"
376    fi
377
378    rndc_key_generator
379
380    if ! ${OCF_RESKEY_named} -u ${OCF_RESKEY_named_user} $root_dir_opt ${OCF_RESKEY_named_options}
381    then
382        ocf_exit_reason "named failed to start."
383        return $OCF_ERR_GENERIC
384    fi
385
386
387    pid=`named_getpid`
388
389    if [ -n "$pid" ]; then
390        if [ ! -e ${OCF_RESKEY_named_pidfile} ]; then
391            echo $pid > ${OCF_RESKEY_named_pidfile}
392        fi
393    else
394        ocf_exit_reason "named failed to start. Probably error in configuration."
395        return $OCF_ERR_GENERIC
396    fi
397
398    while :
399    do
400        named_monitor && break
401        sleep 1
402        ocf_log debug "named hasn't started yet."
403    done
404    ocf_log info "named has started."
405
406    return $OCF_SUCCESS
407}
408
409#
410# Stop
411#
412
413named_stop () {
414    local timeout
415    local timewait
416
417    named_status || return $OCF_SUCCESS
418
419    $OCF_RESKEY_rndc $OCF_RESKEY_rndc_options stop >/dev/null
420    if [ $? -ne 0 ]; then
421        ocf_log info "rndc stop failed. Killing named."
422        kill `cat ${OCF_RESKEY_named_pidfile}`
423    fi
424
425    if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then
426      # Allow 2/3 of the action timeout for the orderly shutdown
427      # (The origin unit is ms, hence the conversion)
428      timewait=$((OCF_RESKEY_CRM_meta_timeout/1500))
429    else
430      timewait=20
431    fi
432
433    sleep 1; timeout=0 # Sleep here for 1 sec to let rndc finish.
434    while named_status ; do
435        if [ $timeout -ge $timewait ]; then
436            break
437        else
438            sleep 1
439            timeout=`expr $timeout + 1`
440            ocf_log debug "named appears to hung, waiting ..."
441        fi
442    done
443
444    #If still up
445    if named_status 2>&1; then
446        ocf_exit_reason "named is still up! Killing"
447        kill -9 `cat ${OCF_RESKEY_named_pidfile}`
448    fi
449
450    rm -f ${OCF_RESKEY_named_pidfile}
451    return $OCF_SUCCESS
452}
453
454
455# Main part
456
457if [ $# -ne 1 ]; then
458    usage
459    exit $OCF_ERR_GENERIC
460fi
461
462case "$1" in
463    methods)    named_methods
464                exit $?;;
465
466    meta-data)  named_meta_data
467                exit $OCF_SUCCESS;;
468esac
469
470named_validate_all
471rc=$?
472
473[ "$1" = "validate-all" ] && exit $rc
474
475if [ $rc -ne 0 ]
476then
477    case "$1" in
478        stop)    exit $OCF_SUCCESS;;
479        monitor) exit $OCF_NOT_RUNNING;;
480        status)  exit $OCF_NOT_RUNNING;;
481        *)       exit $rc;;
482    esac
483fi
484
485if [ `id -u` -ne 0 ]; then
486    ocf_exit_reason "$0 must be run as root"
487    exit $OCF_ERR_GENERIC
488fi
489
490case "$1" in
491    status)     if named_status
492                then
493                    ocf_log info "named is up"
494                    exit $OCF_SUCCESS
495                else
496                    ocf_log info "named is down"
497                    exit $OCF_NOT_RUNNING
498                fi;;
499
500    monitor)    named_monitor
501                exit $?;;
502
503    start)      named_start
504                exit $?;;
505
506    stop)       named_stop
507                exit $?;;
508    reload)     named_reload
509                exit $?;;
510    *)
511                exit $OCF_ERR_UNIMPLEMENTED;;
512esac
513
514# vim:ts=4:sw=4:et:
515