1#!/bin/sh 2# 3# Local puppet daemon activity checker. 4# 5# $Id: check_puppet 382 2012-07-27 15:36:07Z alexey $ 6 7# Puppet client and Puppet master daemon pidfiles. 8: ${PUPPETD_PIDFILE:="/var/run/puppet/agent.pid"} 9: ${PUPPET_MASTERD_PIDFILE:="/var/run/puppet/master.pid"} 10 11# Where puppetd and puppetmasterd store state associated 12# with the running configuration. 13: ${PUPPETD_STATE:="/var/puppet/state/state.yaml"} 14 15# Timeouts for state mtime in seconds. 16: ${PUPPETD_STATE_WARNING:=3600} 17: ${PUPPETD_STATE_CRITICAL:=36000} 18 19# The default checks triggers. The most popular puppet installation is 20# client-only instance. So puppet master checks disabled by default. 21: ${CHECK_PUPPETD:="yes"} 22: ${CHECK_PUPPET_MASTERD:="no"} 23 24# Prefix output string with one of the following keywords: 25# OK, WARNING, CRITICAL, UNKNOWN. 26: ${OUTPUT_PREFIXED:="no"} 27 28# Configurable options ends here. 29 30# The option string. See getopt(3) for details. 31OPTIONS="hvcCmMp:P:s:e:E:" 32 33# Exit codes. 34STATE_OK=0 35STATE_WARNING=1 36STATE_CRITICAL=2 37STATE_UNKNOWN=3 38STATE_DEPENDENT=4 39 40# usage() and version() are helper functions to work with help2man program. 41# 42# Outputs program usage instructions. 43usage() 44{ 45 _me=$(basename $0) 46 cat << EOF 47${_me} observes for Puppet daemon activity by checking the pid file against the 48process running on the system for both Puppet master daemon and Puppet client. 49In the client mode local configuration freshness is also being checked. 50 51Usage: ${_me} [-h | -v] [OPTIONS] 52 53Options: 54 -h Print this help and then exit immediately. 55 -v Print version and exit. 56 -c Enable puppet client checking mode. (default) 57 -C Do not check puppetd. 58 -m Enable puppet master daemon checking mode. 59 -M Do not check puppet master daemon. (default) 60 -p path-to-pid Running puppet client pid file. You should ensure that 61 user running this plugin have sufficient permissions to 62 access this file. Default is /var/run/puppet/agent.pid 63 -P path-to-pid Puppet master daemon pid file. Default pid file path is 64 /var/run/puppet/master.pid 65 -s path-to-yaml Puppet client local configuration YAML. By default puppet 66 stores it's state in /var/puppet/state/state.yaml 67 -e Configuration expiration warning delay. 68 -E Configuration expiration critical delay. 69 70Report bugs to <alexey@renatasystems.org> 71EOF 72 # Return exitcode given as $1 argument. 73 [ $# -eq 1 ] && exit $1 74 75 # Or exit with OK state. 76 exit 0 77} 78 79# Output version and exit. 80version() 81{ 82 cat <<EOF 83$(basename $0) 1.3 84 85Written by Alexey V. Degtyarev 86EOF 87 exit 0 88} 89 90# Helper function to determine given argument either `yes' or `no'. 91check_yesno() 92{ 93 # Accepts exaclty one argument. 94 [ $# -eq 1 ] || return 2 95 96 case $1 in 97 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0;; 98 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1;; 99 *) return 2; 100 esac 101 102 # Should not be here ever. 103 return 0 104} 105 106# Parse given options with builtin getopts function. 107parse_options() 108{ 109 while getopts ${OPTIONS} option $@; do 110 case ${option} in 111 h) usage ${STATE_UNKNOWN};; 112 v) version;; 113 c) CHECK_PUPPETD="yes";; 114 C) CHECK_PUPPETD="no";; 115 m) CHECK_PUPPET_MASTERD="yes";; 116 M) CHECK_PUPPET_MASTERD="no";; 117 p) PUPPETD_PIDFILE=${OPTARG};; 118 P) PUPPET_MASTERD_PIDFILE=${OPTARG};; 119 s) PUPPETD_STATE=${OPTARG};; 120 e) PUPPETD_STATE_WARNING=${OPTARG};; 121 E) PUPPETD_STATE_CRITICAL=${OPTARG};; 122 ?) echo; usage ${STATE_UNKNOWN};; 123 esac 124 done 125 126 return 0 127} 128 129 130check_options() 131{ 132 # Options -C and -M should not be given together. 133 ( check_yesno ${CHECK_PUPPETD} || \ 134 check_yesno ${CHECK_PUPPET_MASTERD} ) || \ 135 { echo "ERROR: Neither -c nor -m option specified. Nothing to do."; \ 136 return 1; } 137 138 # Check that levels are not conflicts each others. 139 [ ${PUPPETD_STATE_WARNING} -gt ${PUPPETD_STATE_CRITICAL} ] && \ 140 { echo "ERROR: WARNING level must not exceed CRITICAL one."; \ 141 return 1; } 142 143 return 0 144} 145 146# Checks that: 147# 1. pid file given in $1 exists; 148# 2. pid number specified in pidfile is a real process identificator, i.e. 149# process with such number is really exists; 150# 3. this process is running puppetd or puppet masterd application with ruby 151# interpreter; 152# 153# Return codes: 154# 0: puppetd found running (and set variable name in $2 to pid number); 155# 1: Empty pid file name given; 156# 2: Can't read the pidfile (i.e. no such file or no access); 157# 3: Can't find puppet process running with pid from ${PUPPETD_PIDFILE}; 158# 4: Process running with pid ${PUPPETD_PIDFILE} is not a puppet daemon; 159check_process() 160{ 161 # Check the pid file existence. 162 [ ! -z $1 ] || return 1 163 [ ! -f $1 -o ! -r $1 ] && return 2 164 165 # Take the process name and arguments from process tree. 166 # Linux users need to use some other command. 167 _procname="`pgrep -F $1 -l -f`" 168 if [ $? -ne 0 ]; then 169 return 3 170 fi 171 172 _variable=$2 173 174# 14659 /usr/local/bin/ruby18 /usr/local/bin/puppetd --rundir /var/run/puppet 175 set -- ${_procname} 176 177 # Take the basename of interpreter running puppetd instance we got from 178 # process list. 179 _running_interpreter=${2##*/} 180 181 # Preserve the pid 182 _pid=$1 183 184 # And take the interpreter from puppetd executable. 185 read _interpreter < $3 186 case "${_interpreter}" in 187 # strip #! 188 \#!*) _interpreter=${_interpreter#\#!} 189 set -- ${_interpreter} 190 _interpreter=${_interpreter##*/} 191 ;; 192 *) _interpreter="/nonexistent" 193 ;; 194 esac 195 196 # Compare two interpretators from ps and from puppetd executable. 197 if [ ${_running_interpreter} != ${_interpreter} ]; then 198 return 4 199 fi 200 201 eval ${_variable}=${_pid} 202 203 return 0 204} 205 206# Checks: 207# 1. state file ${PUPPETD_STATE} existence. 208# 2. state file mtime against ${PUPPETD_STATE_WARNING} and 209# ${PUPPETD_STATE_CRITICAL} 210# 211# Returns: 212# 0: State file OK. 213# 1: Empty name for state file given. 214# 2: State file was not found (or no access). 215# 3: State file's mtime is older than the WARNING point, but is newer than the 216# CRITICAL one. 217# 4: State file's mtime is older than CRITICAL timepoint. 218check_activity() 219{ 220 # Check the state file existence. 221 [ ! -z ${PUPPETD_STATE} ] || return 1 222 [ ! -r ${PUPPETD_STATE} ] && return 2 223 224 # Take file states. 225 eval `stat -s ${PUPPETD_STATE}` 226 227 local _now=`date +%s` 228 local _diff=`echo ${_now} - ${st_mtime} |bc` 229 PUPPETD_STATE_MTIME=${st_mtime} 230 231 # State mtime is less than PUPPETD_STATE_WARNING? 232 if [ ${_diff} -lt ${PUPPETD_STATE_WARNING} ]; then 233 return 0 234 # State is WARN < state < CRIT ? 235 elif [ ${_diff} -gt ${PUPPETD_STATE_WARNING} -a \ 236 ${_diff} -lt ${PUPPETD_STATE_CRITICAL} ]; then 237 return 3 238 # Critical state. 239 elif [ ${_diff} -gt ${PUPPETD_STATE_CRITICAL} ]; then 240 return 4 241 fi 242 243 return 0 244} 245 246# Sets global state information. If the given state code number is less than 247# existing one (i.e. already set before) - ignore new code silently. This make 248# possible to get the worst code error seen while script goes through several 249# check points. 250set_state() 251{ 252 [ $# -ne 1 ] && return 1 253 254 [ $1 -lt ${STATE} ] || STATE=$1 255 256 return 0 257} 258 259# Checks puppet daemon process status and process the exit code returned. 260# The ${OUTPUT_STR} status variable will contain some status information. 261check_puppetd() 262{ 263 check_process ${PUPPETD_PIDFILE} PUPPETD_PID 264 case $? in 265 # Pid file and process seems to be OK. 266 0) 267 _str="puppet client is running as pid: ${PUPPETD_PID}" 268 set_state ${STATE_OK};; 269 270 # Exit code 1 means unreachable pid file. 271 1) 272 _str="cannot found puppetd pid: UNKNOWN" 273 set_state ${STATE_UNKNOWN};; 274 275 # Codes 2 and 3 telling us puppet daemon is down. 276 2|3) 277 _str="puppet client is not running: CRITICAL" 278 set_state ${STATE_CRITICAL};; 279 280 # Should never been reached. 281 *) 282 _str="plugin error: UNKNOWN" 283 set_state ${STATE_UNKNOWN};; 284 esac 285 286 # Concat nonempty output or set the output string. 287 [ -z "${OUTPUT_STR}" ] && OUTPUT_STR=${_str} || \ 288 OUTPUT_STR="${OUTPUT_STR}, ${_str}" 289 290 return 0 291} 292 293# Checks puppetmaster daemon process status and process the exit code returned. 294# The ${OUTPUT_STR} status variable will contain some status information. 295check_puppet_masterd() 296{ 297 check_process ${PUPPET_MASTERD_PIDFILE} PUPPET_MASTERD_PID 298 case $? in 299 # Pid file and process seems to be OK. 300 0) 301 _str="puppet master is running as pid: ${PUPPET_MASTERD_PID}" 302 set_state ${STATE_OK};; 303 304 # Exit code 1 means unreachable pid file. 305 1) 306 _str="cannot found puppet master pid: UNKNOWN" 307 set_state ${STATE_UNKNOWN};; 308 309 # Codes 2 and 3 telling us puppet daemon is down. 310 2|3) 311 _str="puppet master is not running: CRITICAL" 312 set_state ${STATE_CRITICAL};; 313 314 # Should never been reached. 315 *) 316 _str="plugin error: UNKNOWN" 317 set_state ${STATE_UNKNOWN};; 318 esac 319 320 # Concat nonempty output or set the output string. 321 [ -z "${OUTPUT_STR}" ] && OUTPUT_STR=${_str} || \ 322 OUTPUT_STR="${OUTPUT_STR}, ${_str}" 323 324 return 0 325} 326 327# The main routine. 328# 329# Checks process status and configuration file state and outputs status string 330# exiting with corresponding status code. 331main() 332{ 333 # Set the default values. 334 STATE=${STATE_OK} 335 PUPPETD_PID=-1 336 337 # Check the client daemon. 338 check_yesno ${CHECK_PUPPETD} && check_puppetd 339 340 # Check the master daemon. 341 check_yesno ${CHECK_PUPPET_MASTERD} && check_puppet_masterd 342 343 # This will check the configuration file freshness and set the 344 # appropriate status. 345 check_yesno ${CHECK_PUPPETD} && 346 { check_activity 347 case $? in 348 # Configuration freshness OK. 349 0) 350 _time=`date -j -f %s ${PUPPETD_STATE_MTIME} +"%H:%M, %d %b %Y"` 351 _str="configuration last recieved at ${_time}" 352 set_state ${STATE_OK};; 353 354 # Empty name for state file given? 355 1) 356 _str="cannot found state file: UNKNOWN" 357 set_state ${STATE_UNKNOWN};; 358 359 # Unreachable configuration filename. 360 2) 361 _str="YAML state was not yet recieved: UNKNOWN" 362 set_state ${STATE_UNKNOWN};; 363 364 # Configuration expiring in WARNING state. 365 3) 366 _now=`date +%s`; 367 _time=`echo ${_now}-${PUPPETD_STATE_MTIME} |bc` 368 _str="config overdue in ${_time} seconds: WARNING" 369 set_state ${STATE_WARNING};; 370 371 # Configuration too old. 372 4) 373 _now=`date +%s`; 374 _time=`date -j -f %s ${PUPPETD_STATE_MTIME} +"%H:%M, %d %b %Y"` 375 _str="configuration expired at ${_time}: CRITICAL" 376 set_state ${STATE_CRITICAL};; 377 esac 378 379 # Concat nonempty output or set the output string. 380 [ -z "${OUTPUT_STR}" ] && OUTPUT_STR=${_str} || \ 381 OUTPUT_STR="${OUTPUT_STR}, ${_str}" 382 } 383 384 # Select preferable status prefix. 385 if check_yesno ${OUTPUT_PREFIXED}; then 386 case ${STATE} in 387 ${STATE_OK}) _status_str="OK:";; 388 ${STATE_WARNING}) _status_str="WARNING:";; 389 ${STATE_CRITICAL}) _status_str="CRITICAL:";; 390 ${STATE_UNKNOWN}) _status_str="UNKNOWN:";; 391 *) _status_str="ERROR:";; 392 esac 393 _status_str="${_status_str} ${OUTPUT_STR}." 394 else 395 _status_str="${OUTPUT_STR}." 396 fi 397 398 # Output status, set exit code and exit. 399 echo ${_status_str} 400 return ${STATE} 401} 402 403# Parse and check command line options. 404parse_options $* && check_options || return ${STATE_UNKNOWN} 405 406main 407 408exit $? 409