#!/usr/bin/env bash #set -x ###################################################################### # BACK UP YOUR SYSTEM before using this on a production machine!!!!! ###################################################################### # four parameters to tweak: # 1) This "SRCDISK" is a horrible misnomer! # According to http://www.adminschoice.com/docs/solstice_disksuite.htm : # Creating a RAID5 metadevice from a slice that contains an existing # file system will erase the data during the RAID5 initialization # process. # However, we still look for $SRCDISK slices in /etc/vfstab, when # reconfiguring /etc/vfstab with the name device name(s) SRCDISK=c0t2d0 # 2) The disk that will have data synch'd onto it. This disk must be # greater than or equal to in size to $SRCDISK DSTDISKS="c0t3d0 c0t4d0" # 3) The slice number of the partition that is to hold metadb's. # It'll be the same slice on $SRCDISK and all $DSTDISKS. It should NOT # have a filesystem on it, or at least, not a filesystem that you don't # mind losing all the data in. :) I heard that 5M was enough, but when # I tried to create 3 metadb's in a 10M slice, SVM said there wasn't # enough room. Creating 2 metadb's in a 10M slice worked though. METADEV=7 # do we, or do we not, mirror swap partitions? If we don't mirror, we # get twice as much swap space, but you have to boot singleuser and # comment out a line in /etc/vfstab if there's a disk failure. Legal # values are true and false MIRRORSWAP=true # you probably don't need to tune anything below this, unless perhaps # you have a computer with a *.uci.edu hostname. case "$(uname)-$(uname -r)" in SunOS-5.8) echo Solaris 8, checking for patch... if [ -d /var/sadm/patch/108693-25 ] then echo Good, you have 108693-25. This script was tested with 1>&2 echo this patch 1>&2 elif [ -d /var/sadm/patch/108693-* ] then minor=$(ls /var/sadm/patch/108693-* | head -1 | sed 's#/var/sadm/patch/108693\-\([0-9][0-9]*\)#\1#') if [ "$minor" -lt 25 ] then echo Warning: This script was tested with 108693-25, but you 1>&2 echo appear to have an earlier version: 108693-$minor 1>&2 else # $minor > 25 echo Warning: This script was tested with 108693-25, but you 1>&2 echo appear to have a later version: 108693-$minor 1>&2 echo You are probably in good shape. 1>&2 fi fi ;; SunOS-5.9) echo Solaris 9, good 1>&2 ;; SunOS-*) echo Warning, running on a totally untested version of Solaris 1>&2 ;; *) echo Sorry, you need SunOS to use this script 1>&2 exit 1 ;; esac if [ -d /usr/opt/SUNWmd ] then PATH=$PATH:/usr/opt/SUNWmd export PATH fi for prog in metastat metadb metaclear metainit metattach do # note that we're using type, because which didn't give a useful exit # status if type "$prog" > /dev/null 2>&1 then : Good, we have it else echo Sorry, you will need "$prog" on your '$PATH' for $0 to work 1>&2 echo properly.... 1>&2 exit 1 fi done echo Good, you appear to have all the required programs on your '$PATH'. 1>&2 for prog in metachk do # note that we're using type, because which didn't give a useful exit # status if type "$prog" > /dev/null 2>&1 then : Good, we have it else echo Warning, you do not have "$prog" on your '$PATH'. 1>&2 echo You probably want it. 1>&2 fi done # the stuff in this "if" is just for testing - we delete what we've # created so we can start over again. Oh, and also, we override what we # specified above, for specific machines. :) echo case "`uname -n`" in @bingy.nac.uci.edu) if metastat > /dev/null 2>&1 then echo Removing previous SVM setup metadb -f -d /dev/dsk/c0t2d0s7 metadb -f -d /dev/dsk/c0t3d0s7 metadb -f -d /dev/dsk/c0t4d0s7 metaclear -f /dev/md/dsk/d6 # this stuff is more relevant for mirroring, where you need 2. # but in a 3-disk RAID 5, you don't need 3. #metaclear -f /dev/md/dsk/d16 #metaclear -f /dev/md/dsk/d26 #metaclear -f /dev/md/dsk/d36 cp /etc/vfstab.SVM.verybeginning /etc/vfstab fi echo Overriding defaults for bingy SRCDISK=c0t2d0 DSTDISKS="c0t3d0 c0t4d0" METADEV=7 MIRRORSWAP=true ;; bingy.nac.uci.edu) # this one is for the hot spare testing... if metastat > /dev/null 2>&1 then echo Removing previous SVM setup metadb -f -d /dev/dsk/c0t1d0s7 metadb -f -d /dev/dsk/c0t2d0s7 metadb -f -d /dev/dsk/c0t4d0s7 metadb -f -d /dev/dsk/c0t5d0s7 metaclear -f /dev/md/dsk/d6 # this stuff is more relevant for mirroring, where you need 2. # but in a 3-disk RAID 5, you don't need 3. #metaclear -f /dev/md/dsk/d16 #metaclear -f /dev/md/dsk/d26 #metaclear -f /dev/md/dsk/d36 cp /etc/vfstab.SVM.verybeginning /etc/vfstab fi echo Overriding defaults for bingy SRCDISK=c0t2d0 DSTDISKS="c0t1d0 c0t4d0" METADEV=7 MIRRORSWAP=true ;; meter.eng.uci.edu) # this one is for the hot spare testing... if metastat > /dev/null 2>&1 then # Slot 0 is c0t0d0 - filled Thu Apr 7, 2005 # Slot 1 is c0t8d0 - disk 1 # Slot 2 is c0t9d0 - filled Thu Apr 7, 2005 # Slot 3 is c0t10d0 - disk 4 # Slot 4 is c0t11d0 - filled Thu Apr 7, 2005 # Slot 5 is c0t12d0 - disk 5 echo Removing previous SVM setup # these three disks have an identical number of blocks in them, Thu Apr 7, 2005 metadb -f -d /dev/dsk/c0t8d0s7 metadb -f -d /dev/dsk/c0t10d0s7 metadb -f -d /dev/dsk/c0t12d0s7 metaclear -f /dev/md/dsk/d6 cp /etc/vfstab.SVM.verybeginning /etc/vfstab fi echo Overriding defaults for bingy SRCDISK=c0t8d0 DSTDISKS="c0t10d0 c0t12d0" METADEV=7 MIRRORSWAP=true ;; *) echo Using defaults, no host case ;; esac # $1 is a partition # returns just the slice number (EG 0 for slice 0) function get_slice { var="$(echo $1 | sed 's/^.*s\([0-9][0-9]*\)$/\1/')" echo "$var" } # $1 is a partition # returns just the target number (EG 2 for target 2) function get_target { var="$(echo $1 | sed 's/^.*t\([0-9][0-9]*\).*$/\1/')" echo "$var" } # $1 is a partition # returns just the controller number (EG 1 for controller 1) function get_controller { var="$(echo $1 | sed 's/^.*c\([0-9][0-9]*\).*$/\1/')" echo "$var" } # $1 destination disk # $2 source partition # returns corresponding dest partition function dst_from_src { s=s"$(get_slice $2)" echo "$1""$s" } # useless cat, but oh well :) SRCPARTITIONS="$(cat /etc/vfstab | grep "^/dev/dsk/${SRCDISK}s" | \ if [ "$MIRRORSWAP" = true ] then # just pass all dsk device files awk ' { print $1 } ' else # pass all disk device files but swap partitions awk ' { if ($4 != "swap") print $1 }' fi | \ sed 's#/dev/dsk/##')" if [ "$SRCPARTITIONS" = "" ] then ( echo Error: No source partitions in /etc/vfstab echo Is the disk you are trying to use for seed slice numbers in vfstab\? ) 1>&2 exit 1 fi echo SRCDISK is "$SRCDISK" echo DSTDISKS is "$DSTDISKS" echo METADEV is "$METADEV" echo MIRRORSWAP is "$MIRRORSWAP" # we want this one rewritten to all one line, so no quotes... echo SRCPARTITIONS is $SRCPARTITIONS echo echo "`tput smso 2> /dev/null`"ALL DATA on $SRCDISK $DSTDISKS will echo be lost!"`tput rmso`" echo echo Hit interrupt now if you do not want these values! echo Sleeping for 10 seconds... sleep 10 echo echo 'We begin! :)' echo # unmount our source partitions... Not required for root mirroring, but # is required for RAID 5 (with SVM in both cases) for srcpart in $SRCPARTITIONS do echo Trying to umount $srcpart... 1>&2 if umount -f $srcpart then echo Succeeded... 1>&2 else echo Failed, but continuing anyway... 1>&2 fi done # save a copy of the DSTDISK's previous partitioning, in case there's # lost disk space for DSTDISK in $DSTDISKS do prtvtoc /dev/rdsk/"$DSTDISK"s2 > /var/tmp/${DSTDISK}s2 done # copy partition table. s2 is the entire disk, by convention, starting # with the disk label method=1 if type dup-label > /dev/null then method=2 fi case "$method" in 0) # this complains about cylinder boundaries if your disks don't # have pretty similar geometries for DSTDISK in $DSTDISKS do prtvtoc /dev/rdsk/"$SRCDISK"s2 | fmthard -s - /dev/rdsk/"$DSTDISK"s2 done ;; 1) # this works, but it also has the disadvantage of copying the disk # type to the other disk. So a 200G maxtor could end up telling # admins it's a 100G seagate, or something like that. However, sun # format's autoconfigure option can restore the label. # # Does this cause bad magic number errors in dmesg sometimes? echo Bummer, no dup-label program available, using dd 1>&2 for DSTDISK in $DSTDISKS do dd if=/dev/dsk/"$SRCDISK"s2 of=/dev/dsk/"$DSTDISK"s2 count=16 done ;; 2) # AFAIK, this is less confusing to future admins :) echo Cool, you have dup-label 1>&2 for DSTDISK in $DSTDISKS do if ! dup-label /dev/dsk/"$SRCDISK"s2 /dev/dsk/"$DSTDISK"s2 then echo dup-label "$SRCDISK"s2 "$DSTDISK"s2 failed 1>&2 exit 1 fi done ;; esac # Set up metadb's. Writing this to an actual filesystem is a no-no number=$(expr $(echo $DSTDISKS | wc -w) + 1) metadevices="/dev/dsk/${SRCDISK}s$METADEV $(echo $DSTDISKS | tr ' ' '\012' | sed -e 's#$#s'"$METADEV"'#' -e 's#^#/dev/dsk/#')" echo '$metadevices' is $metadevices # since we have >= 3 disks (a requirement for RAID 5), we only need # one metadb per device, hence the -c1 (but 1 is the default anyway) for metadev in $metadevices do if ! metadb \ -a \ -f \ -c 1 \ $metadev then echo error "metadb'ing" $metadev 1>&2 exit 1 fi done # set up meta devices. # dx's are mirror devices # d1x's are source devices # dnx's, n >= 2, are destination devices for srcpart in $SRCPARTITIONS do slice="$(get_slice $srcpart)" # do src device srcmetadev=d1"$slice" # example from http://www.adminschoice.com/docs/solstice_disksuite.htm ## metainit d45 -r c2t3d0s2 c3t0d0s2 c4t0d0s2 # d45: RAID is setup # it appears that -f (force, even if filesystem is mounted) does not # work with RAID 5 if metainit d"$slice" -r ${SRCDISK}s${slice} $(for DSTDISK in $DSTDISKS; do echo "${DSTDISK}s${slice}"; done) then : else echo metainit failed 1>&2 exit 1 fi done # backup vfstab. mostrecent is changed every time we run this script. # verybeginning is only created the first time we run this script. touch /etc/lvm/md.tab for file in vfstab lvm/md.tab do cp /etc/"$file" /etc/"$file".SVM.mostrecent if [ -f /etc/"$file".SVM.verybeginning ] then : else cp /etc/"$file" /etc/"$file".SVM.verybeginning fi done # transform this: #/dev/dsk/c1t0d0s5 /dev/rdsk/c1t0d0s5 /export/home ufs 2 yes logging # ...into: #/dev/md/dsk/d05 /dev/md/rdsk/d05 /export/home ufs 2 yes logging # ...just for example while read line do if echo "$line" | grep '^#' > /dev/null then # preserve comments echo "$line" else set $line # a little sanity checking if [ "$#" = 7 ] && \ echo "$1" | grep "^/dev/dsk/${SRCDISK}s" > /dev/null && echo "$2" | grep "^/dev/rdsk/${SRCDISK}s" > /dev/null then echo "# $line" DSK=/dev/md/dsk/d"$(get_slice $1)" RDSK=/dev/md/rdsk/d"$(get_slice $2)" echo "$DSK" "$RDSK" "$3" "$4" "$5" "$6" "$7" elif [ "$#" = 7 ] && \ echo "$4" | egrep "^swap$" > /dev/null && \ echo "$1" | egrep "^/dev/dsk/${SRCDISK}s" > /dev/null then if [ "$MIRRORSWAP" = true ] then echo "# Swap is mirrored" echo "/dev/md/dsk/d$(get_slice $1) $2 $3 $4 $5 $6 $7" else # set up swap on all disks. We intentionally do not mirror # them. $SRCDISK most likely won't have swap partitions, # but we try to do them anyway. We set up identical # partitions on each of the $DSTDISKS, and swap on all of them. Be # careful not to do swap -files-, only swap partitions echo "# swap partitions are not mirrored, but we swap on both" echo "# disks" echo "$1 $2 $3 $4 $5 $6 $7" # a swap line looks like: # /dev/dsk/c0t0d0s1 - - swap - no - for DSTDISK in $DSTDISKS do echo "/dev/dsk/${DSTDISK}s$(get_slice $1) $2 $3 $4 $5 $6 $7" done fi elif [ "$#" = 7 ] && \ echo "$1" | egrep "/dev/md/dsk/|/dev/md/rdsk" > /dev/null then # Echo any other md lines echo "$line" else # and just copy through anything else, unadorned echo "$line" fi fi done < /etc/vfstab > /etc/vfstab.new mv /etc/vfstab.new /etc/vfstab # this appeases metachk metastat -p > /etc/lvm/md.tab # this is similar to sync, but more effective. It doesn't return until # buffers are flushed, and it also rolls data out of the logs into the # filesystem proper on log structured filesystems echo Running lockfs to sync up buffered/logged data... lockfs -fa echo echo "Done." echo echo "You should now inspect /etc/vfstab, /etc/system, and run the" echo "metastat command, and if all looks well, you should reboot." echo echo "A copy of your DSTDISK's previous partitionings are in" echo "/var/tmp/*s2". If your DSTDISKS is larger than your echo "SRCDISK, you may be able to recover some (non-RAID'd) disk space" echo "on $DSTDISK with some fancy partitioning, but that's beyond the" echo "scope of this program, at least at this time."