#! /bin/sh # # mirrorRoot - mirrors the root disk cleanly and safely # ################################################################ # Author: Todd Stansell # $Id: mirrorRoot,v 1.22 2006-04-19 05:33:21+00 todd Exp $ # # Notes: # # 1) I use vxprint -F ... throughout this script because of the # fact that vxprint -ht output is not consistent between versions. # Version 3.1 adds information about replication (since it's # now integrated). Anyway, by pulling only the information # I need, I can be sure it will work without regard to version. # # 2) status codes of all veritas commands are documented in the # vxintro manpage. # # 3) versions previous to 1.18 don't deal with the Block0 # subdisk correctly for VxVM 3.5. So, make sure you're # using version 1.18 or newer. You wouldn't see this # message if you weren't, though. # ################################################################ mirror_dev="c0t1d0" mirror_name="rootdisk2" verbose=0 c_opt=false setswaponly=false PATH=$PATH:/usr/sbin:/etc/vx/bin PROG=`basename $0` USAGE="usage: $PROG [-hvc] [ [-s] | [-n name] [-d device] ] -h help -s create swap slices and configure dump device only -v increase verbosity level (two -v's for max) -c show vx commands as they are run -n name disk access name of mirror disk (default is $mirror_name) -d device device used to mirror onto (default is $mirror_dev)" # # kills off any processes that are running in the background # cleanup() { vxtask abort $PROG } # # echo a message if we want vx command verbosity # show() { [ $c_opt ] && /bin/echo "# $@" ; } # # echo a message if we want verbosity # msg() { [ $verbose -ge 1 ] && /bin/echo "$@" ; } # # usage: showstatus $value $prog $msg # showstatus() { status="$1" prog="$2" if [ $status -ne 0 ] ; then echo "$3" >&2 echo " $prog exited with status $status" >&2 exit $status fi } # # Check to see that $device has at least $size sectors # usage: check_size $device $size # check_size() { len=`vxprint -g rootdg -e 'sd && sd_disk="'$dm_of_rootvol'" && p0 = "PRIVATE"' -F "%len" | awk '{a=a+$1}END{print '$2'-a}'` pub_len=`vxdisk list $1 | awk '/^public:/ {print}' | sed -e 's/^.* len=\([0-9]*\).*/\1/'` if [ $pub_len -lt $len ] ; then echo "$mirror_dev is only $pub_len sectors, while" >&2 echo "$dm_of_rootvol uses $len sectors. Please choose a larger disk." >&2 exit 7 fi } # # usage: initdisk $device # initdisk() { dev=$1 # see if we need the old_layout option to vxdisksetup. This # is required if we have a fully-utilized disk that has been # encapsulated, so that only 1 cylinder is used for the private # region, and the new disk will have enough public space to # mirror everything to. If you don't want it to use this option, # then initialize the disk before running this script. opt="format=sliced" if grep old_layout /etc/vx/bin/vxdisksetup >/dev/null ; then opt="old_layout" fi msg "Initializing $dev for use with volume manager" show "vxdisksetup -i $dev $opt" vxdisksetup -i $dev $opt; status=$? showstatus $status vxdisksetup "Could not initialize $dev" } # # adds a device ($dev) to a disk group ($dg) with specified media name ($dm) # # usage: add_to_dg $dev $dm [$dg] # add_to_dg() { dg=rootdg dev=$1 dm=$2 [ "$3" ] && dg=$3 msg "Adding $dev to $dg as $dm" show "vxdg -g $dg adddisk $dm=$dev" vxdg -g $dg adddisk $dm=$dev; status=$? showstatus $status vxdg "Could not add $dev to $dg as \"$dm\"" } # # Display the running tasks with the current date # # usage: showtasks # showtasks() { echo ============= `date` ============= vxtask -l list $PROG | egrep "Operation|Work|Progress|^$" } # # simply waits for all tasks with our taskid ($PROG) have finished # # usage: taskwait # taskwait() { out=`vxtask list $PROG 2>&1` status=$? i=0; o=`echo "$out" | wc -l` [ $status -eq 0 -a $verbose -ge 2 ] && showtasks while [ $status -eq 0 ] ; do if [ $verbose -ge 2 ] ; then u=`echo "$out" | wc -l` if [ $u -ne $o ] ; then showtasks o=$u fi j=`expr $i % 4` case $j in 0) /bin/echo '|\r\c' ;; 1) /bin/echo '/\r\c' ;; 2) /bin/echo '-\r\c' ;; 3) /bin/echo '\\\r\c' ;; esac i=`expr $i + 1` fi sleep 2 out=`vxtask list $PROG 2>&1` status=$? done } # # usage: mirrorto $dmname # mirrorto() { dm=$1 # # We start by mirroring rootvol, since it's special # msg "Starting mirror of rootvol to $dm..." show "vxrootmir -t $PROG $dm" vxrootmir -t $PROG $dm ; status=$? showstatus $status vxrootmir "Could not mirror rootvol to $dm" # # Now mirror the rest of the volumes. # for vol in $vols_on_dm ; do if [ $vol = "rootvol" ] ; then continue fi msg "Starting mirror of $vol onto $dm..." show "vxassist -b -g rootdg -t $PROG mirror $vol $dm" vxassist -b -g rootdg -t $PROG mirror $vol $dm; status=$? showstatus $status vxassist "Mirror of $vol onto $dm FAILED" done taskwait } # # make sure swap has a valid partition entry so # we can use it as a dump device, and also set # dumpadm to use that new slice. # setSwap() { swap=`vxprint -g rootdg -e 'sd && assoc.assoc.v_use_type="swap"' -F '%name'` if [ -n "swap" ] ; then msg "Making sure $swap has valid partition entries." slice="" for sd in $swap ; do # vxmksdpart -g rootdg show "vxmksdpart -g rootdg $sd 1 0x03 0x01" vxmksdpart -g rootdg $sd 1 0x03 0x01 if [ -z "$slice" ] ; then slice="`vxprint -g rootdg -F '%device_tag' $sd`s1" fi done if [ -f /etc/dumpadm.conf ] ; then if [ -n "$slice" ] ; then msg "Configuring dumpadm for swap slice $slice" show "dumpadm -d /dev/dsk/$slice" dumpadm -d /dev/dsk/$slice else echo "Could not determine slice to use for dump device!" >&2 fi fi fi } ############################################# # BEGIN MAIN PROGRAM ############################################# # # lets try to clean things up if we die # trap cleanup 1 2 3 5 15 while getopts vcsn:d:h c; do case $c in v) verbose=`expr $verbose + 1` ;; c) c_opt="true" ;; n) mirror_name=$OPTARG ;; d) mirror_dev=$OPTARG ;; s) setswaponly=true ;; *) echo "$USAGE" >&2 exit 64 ;; esac done shift `expr $OPTIND - 1` ######### # Make sure a bunch of stuff is ok before we begin ######### # make sure we're root if /bin/id | grep -v '^uid=0(' >/dev/null ; then echo "$PROG: must be run as root" >&2 exit 64 fi # make sure vxvm is installed if pkginfo VRTSvxvm >/dev/null 2>&1 ; then version=`pkginfo -x VRTSvxvm | tail -1 | sed 's/.*) \(.\).*/\1/'` else echo "$PROG: VRTSvxvm not installed" >&2 exit 64 fi # make sure vxvm is 3.x or greater if [ -z "$version" -o $version -lt 3 ] ; then echo "$PROG: This script requires VRTSvxvm 3.x or greater to function." >&2 exit 64 fi # make sure we're encapsulated vxprint -g rootdg rootvol >/dev/null if [ $? -ne 0 ] ; then echo "$PROG: rootvol doesn't exist. This probably means" >&2 echo "your root disk is not encapsulated yet." >&2 exit 64 fi if $setswaponly ; then setSwap exit fi ######### # Now we should be ready to go... ######### # # Find out where we're mirroring from, and what that includes. # dm_of_rootvol=`vxprint -r -g rootdg -F "%rtype %name" rootvol | \ awk '/^dm/{print $NF; exit}'` dev_of_rootvol=`vxprint -g rootdg -F "%device_tag" $dm_of_rootvol` msg "using $dm_of_rootvol to determine volumes to mirror..." sds_on_dm=`vxprint -g rootdg \ -e 'sd_disk = "'$dm_of_rootvol'"' \ -F "%name" | sort` plexs_on_dm=`vxprint -g rootdg \ -e 'pl_subdisk.sd_disk = "'$dm_of_rootvol'"' \ -F "%name" | sort` vols_on_dm=`vxprint -g rootdg \ -e 'v_plex.pl_subdisk.sd_disk = "'$dm_of_rootvol'"' \ -F "%name" | sort` msg " volumes to be mirrored: "$vols_on_dm size_of_dm=`vxprint -g rootdg -F "%len" $sds_on_dm | awk '{a=a+$1}END{print a}'` msg " total space required: $size_of_dm sectors" # # Check if $mirror_dev is being used already. # msg "Checking \"$mirror_dev\" for use as destination mirror..." for dg in `vxdg -q list | grep -v rootdg | awk '{print $1}'` ; do dev=`vxprint -d -g $dg -F "%name %device_tag" | \ awk '$NF=="'$mirror_dev'" {print $1}'` if [ ! -z "$dev" ] ; then echo "$mirror_dev is used in the $dg disk group as $dev" >&2 exit 5 fi done # # We check to see if $mirror_dev is part of rootdg # dm_mirror=`vxprint -d -g rootdg -F "%name %device_tag" | \ awk '$NF=="'$mirror_dev'" {print $1}'` if [ -n "$dm_mirror" ] ; then # # $mirror_dev is already part of rootdg, now see if it's being used # sd_num=`vxprint -g rootdg -F "%sd_num" $dm_mirror` if [ $sd_num -gt 0 ] ; then echo "$dm_mirror ($mirror_dev) already belongs to rootdg and" >&2 echo "contains $sd_num subdisks. Please choose a different" >&2 echo "device to use or clean out $mirror_dev" >&2 exit 6 fi # # verify space requirements # check_size $mirror_dev $size_of_dm else # # the device ($mirror_dev) is not part of a disk group. Now we # check if it is currently initialized. If not, we initialize it. # Then, we make sure it's part of rootdg. # dm_mirror=$mirror_name show "vxdisk list $mirror_dev" vxdisk list $mirror_dev | grep "^private:" >/dev/null; status=$? if [ $status -eq 0 ] ; then # # the disk is initialized already # msg "$mirror_dev is already initialized and unallocated." else initdisk $mirror_dev fi # # verify device is large enough # check_size $mirror_dev $size_of_dm # # now add the disk to rootdg # add_to_dg $mirror_dev $dm_mirror rootdg fi # # At this point, we have an initialized, clean disk in rootdg # named $dm_mirror that is large enough to hold all of the volumes. # mirrorto $dm_mirror # # Now we need to check if the original disk was an encapsulated # one, or if it was already converted to an initialized one. # priv=`vxprint -g rootdg -e 'sd && \ sd_disk="'$dm_of_rootvol'" && \ putil0!=""' \ -F "%name"` # $priv should now be something like "rootdiskPriv" or "rootdisk1Priv" if [ -n "$priv" ] && echo $sds_on_dm | grep "$priv" >/dev/null ; then msg "$dm_of_rootvol appears to be encapsulated, continuing..." else msg "$dm_of_rootvol does not appear to be encapsulated. We're done." exit 0 fi msg "Removing all mirrors that use $dm_of_rootvol..." for vol in $vols_on_dm ; do msg " removing from $vol...\c" show "vxassist -g rootdg remove mirror $vol \!$dm_of_rootvol" vxassist -g rootdg remove mirror $vol \!$dm_of_rootvol; status=$? if [ $status -ne 0 ] ; then msg "FAILED" fi showstatus $status vxassist "Failed to remove mirror of $vol" msg "ok" done # # remove the encapsulation private subdisk (typically, rootdiskPriv) # if [ -z "$priv" ] ; then echo "No private subdisk found? This isn't normally the case." else if vxprint -g rootdg -e 'sd && sd_name="'%priv'"' >/dev/null ; then show "vxedit -g rootdg rm $priv" vxedit -g rootdg rm $priv status=$? showstatus $status vxedit "Failed to remove $priv subdisk from rootdg" fi fi # # remove disk from rootdg # msg "Removing $dm_of_rootvol from rootdg..." show "vxdg -g rootdg rmdisk $dm_of_rootvol" vxdg -g rootdg rmdisk $dm_of_rootvol; status=$? showstatus $status vxdg "Failed to remove $dm_of_rootvol from rootdg" # # re-initialize the disk # initdisk $dev_of_rootvol # # add the disk to rootdg # add_to_dg $dev_of_rootvol $dm_of_rootvol rootdg # # mirroring volumes. # mirrorto $dm_of_rootvol setSwap