#!/bin/sh ################################################################################ # # SCRIPT: vgsnapshot # VERSION: 1.0 # AUTHOR: Dave Thompson, NSS, ISD, Lee County Clerk of Courts # RELEASE DATE: 07/22/2005 # # DESCRIPTION: vgsnapshot allows user to preview or execute vgexports. It is # designed to work in conjunction with vgrecover and load_balance. # ################################################################################ # Variables. IFS=" " export PATH="/usr/sbin:/usr/bin:/sbin:" SCRIPT="vgsnapshot" USER=`who am i | awk '{print $1}'` WORKING_DIR="/tmp/sysadmin/vg" MAP_DIR="${WORKING_DIR}/maps" GROUP_DIR="${WORKING_DIR}/groups" TAB_DIR="${WORKING_DIR}/tabs" VG_DIR="${WORKING_DIR}/vgdisplays" LVMTAB="/etc/lvmtab" FSTAB="/etc/fstab" LOG="${WORKING_DIR}/vg.log" umask 022 # Function to log messages to syslog. log_it() { logger -p user.info -t ${SCRIPT} "NOTICE (from ${USER}): ${1}" } # Function to determine current date and time. dateit() { DATE=`date +%Y-%m-%d_%H:%M:%S` } # Function to send stdout and stderr to stdout and to the log. tee_it() { ${1} 2>&1 | tee -a ${LOG} } # Function to write output to the user's screen and to the log, including # the current date and time, each time it is invoked. screen_log() { dateit tee_it "echo ${DATE}: ${1}" } # Set trap to send e-mail if this script is killed. kill_do() { MSG="${SCRIPT} abruptly killed" log_it "${MSG}" screen_log "${MSG}" exit 1 } trap kill_do 1 2 3 15 30 # Ensure variables that may be pre-set in the user's shell are not passed to # this script. PREVIEW="NULL" EXECUTE="NULL" # Function to display help output. help() { echo " Usage: ${SCRIPT} -[h|p|x] h help (this output) p preview mode only x execute vgexport A single argument is required. Multiple arguments are prohibited. ${SCRIPT} cycles through all volume groups, except vg00, and allows you to either preview or execute vgexports. When previewing, all volume groups are automatically selected. However, when actually exporting volume groups, you are prompted for each volume group, so no exports are performed without your explicit permission. ${SCRIPT} is designed to work in conjunction with vgrecover, which executes vgimports for any volume groups that have been vgexported; and load_balance, which balances the load across controllers after volume groups have been re-imported. When preparing for a potential change in the device paths to the volume groups, use the preview option, which creates the necessary files that vgrecover requires in order to import the volume groups after the device paths change. The execute option typically will not be required for system maintenance. As a precaution, ${SCRIPT} generates copies of ${LVMTAB} and ${FSTAB}, and it creates files containing verbose output from vgdisplay for all volume groups. When previewing or executing vgexports, ${SCRIPT} performs the following: 1. Creates copies of ${LVMTAB} and ${FSTAB}. 2. Saves a copy of the group file for each selected volume group. 3. Writes physical volume information to a file for each selected volume group. 4. Writes a file containing the contents of \`vgexport -v\` for each selected volume group. 5. Writes a map file for each selected volume group. When executing vgexports, ${SCRIPT} performs the following additional steps: 6. Attempts to unmount all filesystems for each selected volume group. 7. If a filesystem cannot be unmounted, ${SCRIPT} presents a list of processes that are accessing the filesystem. 8. If all filesystems were successfully unmounted for the selected volume group, ${SCRIPT} deactivates the selected volume group. 9. Exports the selected volume group. All output files are written to sub-directories under ${WORKING_DIR}: o Map files are written to ${MAP_DIR}. o Files with vgdisplay information are written to ${VG_DIR}. o Copies of ${LVMTAB} and ${FSTAB} are written to ${TAB_DIR}. o Copies of group files for each volume group are written to ${GROUP_DIR}. " | more } # Function to create directories. make_dir() { DIR="${1}" if [ ! -d "${DIR}" ]; then screen_log "${DIR} does not exist. Creating..." mkdir -p ${DIR} screen_log "Done." fi } # Function to insert a blank line into stdout and log. blank_line() { tee_it "echo " } # Function to save group file and PV information. save_group() { make_dir ${GROUP_DIR} screen_log "Saving ${VG}'s group file to ${GROUP_DIR}..." GROUP_FILE="${GROUP_DIR}/${VG}" ll ${VG_LONG}/group > ${GROUP_FILE} blank_line tee_it "cat ${GROUP_FILE}" blank_line screen_log "Done." } # If the working directory and/or log file don't exist, create them. if [ ! -d "${WORKING_DIR}" ]; then mkdir -p ${WORKING_DIR} fi if [ ! -f "${LOG}" ]; then touch ${LOG} fi # Insert line for dilineation purposes in both the log and to stdout. tee_it "echo ================================================================================ " screen_log "echo ${USER} executing ${SCRIPT}..." blank_line # Validate only one argument is given. if [ ! -n "${1}" ]; then screen_log "ERROR: No argument specified.\n" help exit 1 elif [ -n "${2}" ]; then screen_log "ERROR: Multiple arguments specified.\n" help exit 1 fi # Configure variables based upon argument selected by user. Exit if no # argument specified. if [ "${1}" = "-h" ]; then help exit 0 elif [ "${1}" = "-p" ]; then PREVIEW="${1}" elif [ "${1}" = "-x" ]; then EXECUTE="${1}" else screen_log "ERROR: Improper argument specified.\n" help exit 1 fi # Generate copies of lvmtab and fstab for reference in the case of # major problems. make_dir ${TAB_DIR} screen_log "Copying ${LVMTAB} to ${TAB_DIR}..." dateit LVMTAB_COPY="${TAB_DIR}/${DATE}.lvmtab" blank_line cp ${LVMTAB} ${LVMTAB_COPY} tee_it "ll ${LVMTAB} ${LVMTAB_COPY}" blank_line screen_log "Done." screen_log "Following are ${LVMTAB_COPY}'s contents:" blank_line tee_it "strings ${LVMTAB_COPY}" blank_line screen_log "Copying ${FSTAB} to ${TAB_DIR}..." dateit FSTAB_COPY="${TAB_DIR}/${DATE}.fstab" blank_line cp ${FSTAB} ${FSTAB_COPY} tee_it "ll ${FSTAB} ${FSTAB_COPY}" blank_line screen_log "Done." screen_log "Following are ${FSTAB_COPY}'s contents:" blank_line tee_it "cat ${FSTAB_COPY}" blank_line # Create syslog entry that script is beginning. log_it "${SCRIPT} started" # Create map files and perform exports, if specified. make_dir ${MAP_DIR} for VG in `ls -d /dev/vg* |grep -v vg00 | cut -c6-`; do VG_LONG="/dev/${VG}" dateit MAP_FILE="${MAP_DIR}/${DATE}_${VG}.map" VGEXPORT_OK="YES" # Create file with vgdisplay contents. make_dir ${VG_DIR} dateit VGDISPLAY_OUT="${VG_DIR}/${DATE}_${VG}_vgdisplay.out" screen_log "Writing output of \`vgdisplay -v ${VG_LONG}\` to ${VGDISPLAY_OUT}..." vgdisplay -v ${VG_LONG} > ${VGDISPLAY_OUT} screen_log "Done." # Preview option. if [ "${PREVIEW}" = "-p" ]; then blank_line tee_it "echo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - " screen_log "Begin processing ${VG}..." save_group screen_log "Creating map file ${MAP_FILE} using PREVIEW mode..." blank_line tee_it "vgexport -m ${MAP_FILE} -s -v -p ${VG_LONG}" blank_line screen_log "Map file contents:" blank_line tee_it "cat ${MAP_FILE}" blank_line screen_log "To perform actual exports, execute \"${SCRIPT} -x\"." # Export option. elif [ "${EXECUTE}" = "-x" ]; then screen_log "WARNING: You are about to perform exports on ${VG}. Do you wish to continue [y/n]? \c" read ANSWER if [ "${ANSWER}" = "y" ]; then save_group # Attempt to unmount filesystems. screen_log "Unmounting all filesystems from ${VG}..." for FS in `grep ${VG} /etc/fstab | awk '{print $2}'`; do screen_log "Attempting to unmount ${FS}..." umount ${FS} VGEXPORT_OK="YES" VERIFY_FS=`mount | grep "${FS} " | awk '{print $1}'` if [ "${VERIFY_FS}" = "" ]; then log_it "Unmounted ${FS}" screen_log "Done." else VGEXPORT_OK="NO" screen_log "WARNING: ${FS} could not be unmounted." screen_log "Following are the processes attached to ${FS}:" tee_it "fuser -u ${FS}" screen_log "Ensure any databases associated with ${FS} are shut down and that no users are currently accessing ${FS}." fi done if [ "${VGEXPORT_OK}" = "YES" ]; then VGSTATUS=`vgdisplay ${VG} | grep "VG Status" | awk '{print $3}'` screen_log "Deactivating ${VG}..." if [ "${VGSTATUS}" = "available" ]; then tee_it "vgchange -a n ${VG_LONG}" log_it "Deactivated ${VG}" screen_log "Done." else screen_log "${VG} was already in a deactivated state." fi screen_log "Creating map file ${MAP_FILE} and exporting ${VG}..." tee_it "vgexport -m ${MAP_FILE} -s -v ${VG_LONG}" log_it "Exported ${VG}" screen_log "Done." else screen_log "Because all filesystems couldn't be unmounted, ${VG} cannot be exported." fi else screen_log "You chose not to vgexport ${VG}. Skipping..." fi fi done screen_log "${SCRIPT} is finished. The log is ${LOG}." # Exit. log_it "${SCRIPT} finished" exit 0