#!/bin/sh ################################################################################ # # SCRIPT: load_balance # VERSION: 2.0 # AUTHOR: Dave Thompson, NSS, ISD, Lee County Clerk of Courts # RELEASE DATE: 06/20/2005 # # DESCRIPTION: load_balance balances the load across controllers. # # NEW IN THIS RELEASE: # # 1.1: a. Changed references from disks to logical volumes, as it more # accurately reflects what is being evaluated. # b. Corrected an error that referenced the number of physical disks rather # than the number of logical volumes. # c. Added a feature that writes a notification message to syslog # immediately before performing a vgreduce. # 2.0: a. This is a major re-write of load_balance. Since most everything has # been changed, it's pointless to list all the changes. # ################################################################################ # Variables. IFS=" " export PATH=/usr/bin:/sbin:/usr/sbin: SCRIPT="load_balance" VERSION="2.0" USER=`who am i | awk '{print $1}'` TMP_DIR="/tmp/sysadmin" UNIQ_CTLRS="${TMP_DIR}/.${SCRIPT}_uniq_ctlrs.out" ALL_PV="${TMP_DIR}/.${SCRIPT}_all_pv.out" ALL_PV_STRIPPED="${TMP_DIR}/.${SCRIPT}_all_pv_stripped.out" VG_DISKS="${TMP_DIR}/.${SCRIPT}_vg_disks.out" CTLR_LIST="${TMP_DIR}/.${SCRIPT}_ctlr_list.out" FILELIST="${TMP_DIR}/.${SCRIPT}_filelist.out" # Introduce script to the systems administrator. echo "\n${SCRIPT} ${VERSION} ---------------- ${SCRIPT} shows the primary and alternate paths of logical volumes to their disk devices and allows the paths to be swapped, in order to balance the load across both controllers. However, vg00 is excluded, since it should be on the internal disk drives. ${SCRIPT} can automatically balance the load when the "-auto" flag is used. In order to give the systems administrator more control, when the "-auto" flag is not used, ${SCRIPT} presents the current configuration, and it allows the user to determine which logical volumes should be configured to the alternate path. Usage: ${SCRIPT} [-auto] Press \"return\" to continue...\c\n" read RETURN # Set auto-balance if specified. AUTO="NULL" if [ "${1}" = "-auto" ]; then AUTO="ON" fi # Function to clean up temporary files. cleanup () { echo "\nCleaning up temporary files...\n" echo "${UNIQ_CTLRS}\n${ALL_PV}\n${ALL_PV_STRIPPED}\n${VG_DISKS}\n${CTLR_LIST}\n${FILELIST}" > ${FILELIST} for FILE in `cat ${FILELIST}`; do if [ -f "${FILE}" ]; then echo "Removing ${FILE}..." rm ${FILE} echo "Done." fi done echo "\nAll temporary files have been removed.\n" } # Set trap to clean up and exit if this script is killed. kill_do() { echo "\nWARNING: ${SCRIPT} was abruptly killed." cleanup exit 1 } trap kill_do 1 2 3 15 30 # Function to create a list of the current state of all PV's. all_pv() { if [ -f "${ALL_PV}" ]; then rm ${ALL_PV} fi if [ -f "${ALL_PV_STRIPPED}" ]; then rm ${ALL_PV_STRIPPED} fi for VG_DIR in `ls -d /dev/vg* | grep -v vg00`; do echo "#${VG_DIR}" >> ${ALL_PV} vgdisplay -v ${VG_DIR} | sed -e '1,/Physical volumes/d' -e '/Physical volume groups/,$d'\ | grep "PV Name" | awk '{print $3,$4}' >> ${ALL_PV} done cat ${ALL_PV} | egrep -v "Alt|#" > ${ALL_PV_STRIPPED} } # Create a list of all the PV's. all_pv # Display controllers. cat ${ALL_PV} | grep -v "#" | awk '{print $1}' | sed -e 's/\/dev\/dsk\///' -e 's/t[0-9]d[0-9]//'\ | sort -n | uniq > ${UNIQ_CTLRS} echo "\nControllers:" cat ${UNIQ_CTLRS} # Determine the number of PV's that should be using each controller, and report. echo "\nPV's that should be using each controller as their primary paths: \c" PV_FULL_COUNT=`cat ${ALL_PV_STRIPPED} | wc -l` PV_HALF_COUNT=`expr ${PV_FULL_COUNT} / 2` echo "${PV_HALF_COUNT}" # Assign variables to controllers. N="0" for CONTROLLER in `cat ${UNIQ_CTLRS}`; do N=`expr ${N} + 1` if [ "${N}" = "1" ]; then CTLR1="${CONTROLLER}" > ${CTLR_LIST} elif [ "${N}" = "2" ]; then CTLR2="${CONTROLLER}" >> ${CTLR_LIST} else echo "WARNING: Only two controllers can be used with ${SCRIPT}. Exiting..." cleanup exit 1 fi done # Function to gather stats on both controllers. view_ctlrs() { all_pv echo "\nPV's using ${CTLR1} as the primary path: \c" CTLR1_COUNT=`grep ${CTLR1} ${ALL_PV_STRIPPED} | wc -l` echo ${CTLR1_COUNT} echo "PV's using ${CTLR2} as the primary path: \c" CTLR2_COUNT=`grep ${CTLR2} ${ALL_PV_STRIPPED} | wc -l` echo ${CTLR2_COUNT} if [ "${CTLR1_COUNT}" -gt "${CTLR2_COUNT}" ]; then DOMINANT_CTLR="${CTLR1}" SECONDARY_CTLR="${CTLR2}" DOMINANT_CTLR_COUNT="${CTLR1_COUNT}" SECONDARY_CTLR_COUNT="${CTLR2_COUNT}" elif [ "${CTLR2_COUNT}" -gt "${CTLR1_COUNT}" ]; then DOMINANT_CTLR="${CTLR2}" SECONDARY_CTLR="${CTLR1}" DOMINANT_CTLR_COUNT="${CTLR2_COUNT}" SECONDARY_CTLR_COUNT="${CTLR1_COUNT}" elif [ "${CTLR1_COUNT}" = "${CTLR2_COUNT}" ]; then echo "\n*** THE LOAD IS BALANCED ***\n" echo "Following is the current configuration:" cat ${ALL_PV} echo "" cleanup exit 0 fi DIFF=`expr ${DOMINANT_CTLR_COUNT} - ${SECONDARY_CTLR_COUNT}` HALF_DIFF=`expr ${DIFF} / 2` echo "Difference between ${DOMINANT_CTLR_COUNT} and ${SECONDARY_CTLR_COUNT}: ${DIFF}" echo "PV'S THAT SHOULD BE MOVED FROM ${DOMINANT_CTLR} TO ${SECONDARY_CTLR}: *** ${HALF_DIFF} ***" if [ "${HALF_DIFF}" = "0" ]; then echo "\n*** THE LOAD IS BALANCED ***\n" echo "Following is the current configuration:\n" cat ${ALL_PV} echo "" cleanup exit 0 fi } # Process PV's. for VG in `ls -d /dev/vg* | grep -v vg00`; do VG_UPPER=`echo ${VG} | tr [a-z] [A-Z]` echo "\n=====================================================================================\n" view_ctlrs echo "\n=====================================================================================\n" echo "Processing ${VG_UPPER}...\n" VG_SHORT=`echo ${VG} | cut -c6-` cat ${ALL_PV} | sed -e '1,/'${VG_SHORT}'/d' -e '/#/,$d' | grep "${DOMINANT_CTLR}" | egrep -v "Alt"\ | awk '{print $1}' > ${VG_DISKS} if [ ! -s "${VG_DISKS}" ]; then echo "All disks in ${VG_SHORT} are already pointing to the non-dominant controller. Skipping..." else echo "Disks on primary paths in ${VG} that may be moved to alternate paths:" cat ${VG_DISKS} fi for DISK_PATH in `cat ${VG_DISKS}`; do view_ctlrs if [ "${AUTO}" != "ON" ]; then echo "\n${DISK_PATH}: Move to alternate path [y/n]? \c" read MOVE else MOVE="y" if [ "${MOVE}" = "y" ]; then logger -p user.info -t ${SCRIPT} "NOTICE (from ${USER}): Moving ${DISK_PATH} to alternate link..." echo "\n${DISK_PATH}: Removing as a primary path in ${VG}..." vgreduce ${VG} ${DISK_PATH} if [ "${?}" = "0" ]; then vgextend ${VG} ${DISK_PATH} if [ "${?}" = "0" ]; then echo "${DISK_PATH}: Successfully moved from the primary to the alternate path." else echo "FAILURE: ${DISK_PATH}: Couldn't add back to ${VG}." fi else echo "FAILURE: ${DISK_PATH}: Couldn't remove from ${VG}." fi echo "${DISK_PATH}: Adding as an alternate link in ${VG}..." else echo "\n${DISK_PATH}: No action requested. Skipping..." fi fi done done # Clean up and exit. cleanup exit 0