#!/bin/sh - # # $Header: /afs/linux.ibm.com/src/afs/@cell/scripts/afs_optim/RCS/afs_optim,v 1.24 2002/10/16 09:59:35 mpb Exp $ # # $Locked$ # # -------------------------------------------------------------------- # # NAME afs_optim # AUTHOR Paul Blackburn http://acm.org/~mpb # WRITTEN October 2000 # # PURPOSE Optimise fileserver preferences. # Given a choice of fileservers for the same ReadOnly # replica volume, which fileserver should be accessed? # # "afs_optim" uses ping to rank fileservers and then # sets the preferences using: "fs setserverprefs" # # "afs_optim" appends its results to a file which # is referenced by the boot time AFS startup file # generated by "afs_quill". This preserves file server # preferences across a reboot. # # For AFS clients installed without afs_quill, add the following # to your AFS boot time run command file (AIX: /etc/rc.afs, # RedHat Linux: /etc/rc.d/init.d/afs) # # /usr/afsws/bin/fs sp -f /usr/vice/etc/afs_fileserver_preferences # # .................................................................... # EXAMPLE FOR AIX: # # Add the following near the end of /etc/rc.afs # # f=/usr/vice/etc/afs_fileserver_preferences # if [ -s ${f} ]; then # /usr/afsws/bin/fs sp -f ${f} # fi # .................................................................... # # TRIED & TESTED: on Linux and AIX 4 # # # NOTES # a) needs root in order to execute "fs setserverprefs" # # HISTORY # 24-Oct-2000 mpb first draft # 31-Oct-2000 mpb added AIXism, now creates the serverprefs in: # /usr/vice/etc/afs_fileserver_preferences # 01-Nov-2000 mpb tidy up output # # # TODO # # port to other UNIXen # # -------------------------------------------------------------------- # let's get defensive PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/afsws/bin:/usr/afsws/etc: IFS=" " unset ENV # Undo any aliases for commands used. # This is to avoid problems such as "ls" having unexpected format output unalias awk 2>/dev/null unalias cat 2>/dev/null unalias cat 2>/dev/null unalias chmod 2>/dev/null unalias cp 2>/dev/null unalias cut 2>/dev/null unalias date 2>/dev/null unalias df 2>/dev/null unalias grep 2>/dev/null unalias head 2>/dev/null unalias ls 2>/dev/null unalias mkdir 2>/dev/null unalias mv 2>/dev/null unalias ping 2>/dev/null unalias rm 2>/dev/null unalias rmmod 2>/dev/null unalias rsync 2>/dev/null unalias sed 2>/dev/null unalias sort 2>/dev/null unalias strings 2>/dev/null unalias tail 2>/dev/null unalias uname 2>/dev/null unalias whois 2>/dev/null # enough defensiveness already! cmd=$(basename ${0}) cmdline="${cmd} $*" tempfile=/tmp/${cmd}_tempfile_$$ tempfile2=/tmp/${cmd}_tempfile2_$$ logdir=/tmp log=${logdir}/${cmd} # site configurable defaults follow ------------------------------------- default_notify=mpb@acm.org default_cellname=linux.ibm.com f=/usr/vice/etc/ThisCell if [ -s "${f}" ]; then default_cellname=$(cat ${f}) else echo "${cmd} fatal error: missing ${f}" >&2 exit 1 fi default_wp=http://www.transarc.com/Library/documentation/afs/3.5/unix/index.htm default_wp=http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/index.htm default_fsp=/usr/vice/etc/afs_fileserver_preferences # end site configurable defaults ---------------------------------------- # functions ------------------------------------------------------------- usage() { cat <+]:w See also: http://www.transarc.ibm.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminRef/auarf162.htm#HDRFS_SETSERVERPREFS ${cmd} [options] where options are: -cell \${cellname} - specify AFS cell name (default is ${default_cellname}) -fsp \${file} - specify fileserver preferences file (default is ${default_fsp}) Please report bugs/suggestions/comments to: ${default_notify} eeooff } fatal() { echo "${cmd} fatal: ${1}" >&2 exit 1 } error() { echo "${cmd} error: ${1}" >&2 } warning() { echo "${cmd} warning: ${1}" >&2 } tstamp() { echo "`date '+''%H''%M'':%S'` ${cmd}: ${1}" } doit() { tstamp "${1}" eval ${1} retcode=$? if [ ${retcode} != 0 ]; then error "\$?=${retcode}" fi } elapsed_time () { start=${1} end=${2} unset x unset y if [ ${start} = ${end} ]; then case ${os} in Linux ) echo -n "zero seconds" ;; AIX ) echo "zero seconds \c" ;; esac return fi let elapsed_seconds=${end}-${start} let days=${elapsed_seconds}/86400 let days_sec=${days}*86400 let x=${elapsed_seconds}-${days_sec} let hours=${x}/3600 let hours_sec=${days}*3600 let x=${days_sec}+${hours_sec} let y=${elapsed_seconds}-${x} let mins=${y}/60 let mins_sec=${mins}*60 let x=${days_sec}+${hours_sec}+${mins_sec} let secs=${elapsed_seconds}-${x} m="" if [ ${days} != 0 ]; then if [ ${days} = 1 ]; then m=${m}"1 day " else m=${m}"${days} days" fi fi if [ ${hours} != 0 ]; then if [ ${hours} = 1 ]; then m=${m}"1 hour " else m=${m}"${hours} hours " fi fi if [ ${mins} != 0 ]; then if [ ${mins} = 1 ]; then m=${m}"1 minute " else m=${m}"${mins} minutes " fi fi if [ ${secs} != 0 ]; then if [ ${secs} = 1 ]; then m=${m}"1 second " else m=${m}"${secs} seconds" fi fi echo ${m} } trap_cleanup () { # tidy up on receipt of signal (ctrl-C, etc) tstamp "trap_cleanup commenced" doit "rm ${tempfile} ${tempfile2}" # doit "unlog" epoch_now=$(date +%s) tstamp "total duration $(elapsed_time ${start_epoch} ${epoch_now})" tstamp "trap_cleanup completed" exit 1 } # main ------------------------------------------------------------------- for signal in 1 2 3 15; do trap "echo \"interrupted by signal ${signal}\"; trap_cleanup; exit 1" ${signal} done case "${1}" in -? | -help | -usage | --?| --help | --usage ) usage exit ;; esac mkdir -p ${logdir} 2>/dev/null # make sure we have a $logdir echo "stdout and stderr now being written to ${log}" exec 4>&2 exec 3>&1 exec 1>${log} exec 2>&1 tstamp "\$Revision: 1.24 $ commenced on $(date '+%a %d %h %y')" # sanity checking starts here ------------------------------------------- #tstamp "Check we are running on supported UNIX" os=$(uname) case "${os}" in Linux ) tstamp "We have Linux" start_epoch=$(date +%s) ;; AIX ) tstamp "We have AIX" h=$(date +%H) m=$(date +%M) s=$(date +%S) let start_epoch=\(${h}*60*60\)+\(${m}*60\)+${s} ;; * ) warning "We have unsupported operating system: ${os}." warning "This may not work." ;; esac #tstamp "Check if we are root" x=$(whoami) if [ "${x}" != 'root' ]; then fatal "You must be logged in as root to run ${cmd}" else tstamp "We are root" fi if [ "${os}" = "Linux" ]; then tstamp "Test for distribution type" distro="unknown" file=/etc/redhat-release if [ -s ${file} ]; then distro="RedHat" fi file=/etc/SuSE-release if [ -s ${file} ]; then distro="SuSE" fi file=/etc/mandrake-release if [ -s ${file} ]; then distro="Mandrake" fi file=/etc/debian_version if [ -s ${file} ]; then distro="Debian" fi file=/etc/slackware-version if [ -s ${file} ]; then distro="Slackware" fi file=/etc/turbolinux-version if [ -s ${file} ]; then distro="TurboLinux" fi # tstamp "Test for Linux distribution version" case ${distro} in RedHat ) distro_version=$(cat /etc/redhat-release) ;; SuSE ) distro_version=$(cat /etc/SuSE-release) ;; Mandrake ) distro_version=$(cat /etc/mandrake-release) ;; Debian ) distro_version=$(cat /etc/debian_version) ;; Slackware ) distro_version=$(cat /etc/slackware-version) ;; TurboLinux ) distro_version=$(cat /etc/turbolinux-version) ;; * ) distro_version="unknown" ;; esac tstamp "Distribution is: ${distro_version}" if [ "${distro_version}" = "Red Hat Linux release 6.2 (Zoot)" ]; then # tstamp "excellent! We have the supported Linux distribution:" distro_v="RedHat 6.2" #else # if [ ${distro} != "Redhat" ]; then # warning "This distribution is not fully supported." # warning "What the heck! We shall try anyway. ;-)" # else # tstamp "This earlier version of RedHat should be OK" # fi fi if [ "${distro_version}" = "7.0.0" ]; then distro_v="Slackware 7" fi #tstamp "Check kernel version" kernel=$(uname -r) #tstamp "kernel is ${kernel}" #tstamp "Check if uni-processor or symetric multi-procesor" x=$(uname -v | awk '{print $2}') if [ "${x}" = "SMP" ]; then smp_kernel="true" # tstamp "symetric multi-procesor" else # tstamp "uni-processor" smp_kernel="false" fi fi #tstamp "Check if we already have AFS" df | grep -q "AFS" if [ $? = 0 ]; then tstamp "We have AFS" else fatal "We are not running AFS. Bye!" fi # crack command line arguments while [ ! -z "${1}" ]; do case ${1} in -cell ) shift if [ -z "${1}" ]; then fatal "missing cell name" else cellname=${1} fi ;; -cell*) cell=$(echo ${1} | cut -c6-) ;; -fsp ) shift if [ -z "${1}" ]; then fatal "missing fileserver prefs file name" else fsp=${1} fi ;; -fsp*) fsp=$(echo ${1} | cut -c6-) ;; -help | -? | -usage ) usage exit ;; *) warning "unknown command line argument: ${1}" usage exit 1 ;; esac shift done if [ -z "${fsp}" ]; then fsp=${default_fsp} fi if [ -z "${cellname}" ]; then cellname=${default_cellname} fi # Check if we can ping first database server for cell ${cellname} # as listed in /usr/vice/etc/CellServDB. # # No point in continuing if database server is un-pingable csdb=/usr/vice/etc/CellServDB if [ ! -s ${csdb} ]; then fatal "missing file: ${csdb}" fi csdb_numbered=/tmp/numbered_CellServDB_$$ < ${csdb} awk '{printf(" %d %s\n",NR, $0)}' > ${csdb_numbered} line=$(grep \>${cellname} ${csdb_numbered} | awk '{print $1}') next_line=$(expr ${line} + 1) srvr=$(grep " ${next_line} " ${csdb_numbered} | awk '{print $2}') rm ${csdb_numbered} tstamp "ping AFS db server: ${srvr}" tstamp "in AFS cell: ${cellname}" case ${os} in Linux ) doit "ping -c 2 -p 36 ${srvr} 2>&1 > /dev/null" ;; AIX ) "ping ${srvr} 56 3 2>&1 > /dev/null" ;; esac if [ $? = 0 ]; then tstamp "pinged ${srvr} OK" else warning "ping ${srvr} failed" fatal "bye" fi # Compute a list of fileservers for AFS cell ${cellname} fileservers=$(vos listvldb -cell ${cellname} \ | awk '(/ server /) {print $2}' \ | sort -u | sed -e "/^$/d") tstamp "fileservers for cell ${cellname} are:" echo "${fileservers}" # If only one fileserver, bail out now count=$(echo "${fileservers}" | wc -l | awk '{print $1}') if [ "${count}" = 1 ]; then warning "AFS cell ${cellname} has only one fileserver" fatal "one fileserver, one preference! enough already! ;-)" fi for server in ${fileservers}; do case ${os} in Linux ) avg=$(ping -c 21 -p 36 ${server} \ | grep "min/avg/max" | awk -F/ '{print $5}') ;; AIX ) avg=$(ping ${server} 56 21 \ | grep "min/avg/max" | awk -F/ '{print $4}') ;; esac if [ -z "${avg}" ]; then warning "failed to compute average ping time for ${server}" else echo "${avg} ${server}" >> ${tempfile} fi done if [ -z "${debug}" ]; then sort -n -o ${tempfile} ${tempfile} else doit "sort -n -o ${tempfile} ${tempfile}" echo doit "cat ${tempfile}" echo fi fsp=/usr/vice/etc/afs_fileserver_preferences date=$(date '+%a %d %h %y') # normalise ping results into range 40000-65500 # example input data (from Linux): # # 435.207 afs-linux-fs2.bstoke.uk.ibm.com # 440.047 afs-linux-fs1.bstoke.uk.ibm.com # 571.004 afs-linux-fs1.raleigh.ibm.com # 737.328 sp15at4.hursley.ibm.com # # and output needed: # # afs-linux-fs2.bstoke.uk.ibm.com 40000 # afs-linux-fs1.bstoke.uk.ibm.com 40409 # afs-linux-fs1.raleigh.ibm.com 51462 # sp15at4.hursley.ibm.com 65500 case "${os}" in Linux ) < ${tempfile} sed -e "s/\.//" -e "s/\. / /" -e "s/ ms / /" >${tempfile2} ;; AIX ) cp ${tempfile} ${tempfile2} ;; esac if [ ! -z "${debug}" ]; then echo doit "cat ${tempfile2}" echo fi tmpfsp=/tmp/afs_fileserver_preferences_$$ < ${tempfile2} awk '{ server[NR] = $2 avg[NR] = $1 } END { printf("##\n") printf("##\ file: %s\n",tmpfsp) printf("##\n") printf("## AFS fileserver preferences for cell %s\n",cell) printf("## automagically generated by %s on %s \n",cmd,date) printf("## Ping results were: \n") printf("##\n") min=65535 max=0 i=1 while ( i < NR+1 ) { printf("## %-50s %5s\n",server[i],avg[i]) if ( avg[i] > max ) { max = avg[i] } if ( avg[i] < min ) { min = avg[i] } i=i+1 } range=max-min printf("##\n## minimum %s maximum %s range %s\n",min,max,range) factor=range/(65500-40000) printf("## normalisation factor for range 40000-65500 is %s\n",factor) printf("##\n") i=1 while ( i < NR+1 ) { diff[i]=avg[i]-min i=i+1 } mindiff=65535 maxdiff=0 i=1 while ( i < NR+1 ) { norm[i]=40000+int((diff[i]/factor)+0.5) printf("#+ %-50s %s\n",server[i],norm[i]) i=i+1 } printf("##\n") printf("## Use the following command to set these preferences:\n") printf("##\n") printf("## /usr/afsws/bin/fs sp -f %s\n",fsp) }' cmd=${cmd} date="${date}" tmpfsp=${tmpfsp} fsp=${fsp} cell=${cellname} > ${tmpfsp} doit "cat ${tmpfsp}" echo sed -e "/^##/d" -e "s/#+ //" < ${tmpfsp} >> ${fsp} # need to be root to run next command doit "fs sp -f ${fsp}" if [ -z "${debug}" ]; then fs getserverprefs > ${tempfile} else doit "fs getserverprefs > ${tempfile}" fi > ${tempfile2} for server in ${fileservers}; do grep ${server} ${tempfile} >> ${tempfile2} done tstamp "AFS fileserver preferences for /afs/${cellname}:" echo case "${os}" in Linux ) sort -n -k52,57 +1 ${tempfile2} ;; AIX ) sort +1 -2 ${tempfile2} ;; esac echo if [ -z "${debug}" ]; then rm ${tempfile} ${tempfile2} else doit "rm ${tempfile} ${tempfile2}" fi case "${os}" in Linux ) end_epoch=$(date +%s) ;; AIX ) h=$(date +%H) m=$(date +%M) s=$(date +%S) let end_epoch=\(${h}*60*60\)+\(${m}*60\)+${s} # # test for case of midnight flip over...if so, add 1 day in seconds (86400) # we assume this script always takes less than 24 hours to complete # If only AIX had "date +%s" sigh. # if [ ${end_epoch} -lt ${start_epoch} ]; then let end_epoch=${end_epoch}+86400 fi ;; esac tstamp "duration $(elapsed_time ${start_epoch} ${end_epoch})"