#!/usr/bin/env bash # you probably want bash or ksh for this. Some /bin/sh's may have # problems with it # future enhancements: # 1) The script may need an installboot... #set -x # error out if an unitialized variable is used set -u # I've used this script on two systems successfully now: one a test # system, and one for a client. :) I feel like this script is # getting very close to done. ###################################################################### # BACK UP YOUR SYSTEM before using this on a production machine!!!!! ###################################################################### # four parameters to tweak: # 1) The disk that starts with data on it SRCDISK=c0t1d0 # 2) The disk that will have data synch'd onto it. This disk must be # greater than or equal to in size to $SRCDISK DSTDISK=c0t2d0 # 3) The slice number of the partition that is to hold metadb's. # It'll be the same slice on $SRCDISK and $DSTDISK. 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 # Allow the user to specify a list of mount points to NOT MIRROR. # Filesystems should be separated by whitespace, like blanks or tabs # Use this if you want to exclude nothing EXCLUDE_MOUNT_POINTS="" # use this if you want to exclude /export/home #EXCLUDE_MOUNT_POINTS="/export/home" # If you just want a mirror that isn't a system disk, set this to false # SYSTEM_DISK=false SYSTEM_DISK=true # you probably don't need to tune anything below this, unless perhaps # you have a computer with a *.uci.edu hostname. 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 echo for prog in metachk dup-label 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 echo case "$(uname)-$(uname -r)" in @SunOS-5.8) # tested with SVM-RAID-5, but not SVM-root-mirror 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 # the stuff in this "case" 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 ark.oir.uci.edu) if metastat > /dev/null 2>&1 then echo Removing previous SVM setup metadb -d /dev/dsk/c0t0d0s5 metadb -f -d /dev/dsk/c0t2d0s5 # metaclear -f /dev/md/dsk/d0 # metaclear -f /dev/md/dsk/d10 # metaclear -f /dev/md/dsk/d20 # metaclear -f /dev/md/dsk/d3 # metaclear -f /dev/md/dsk/d13 # metaclear -f /dev/md/dsk/d23 # metaclear -f /dev/md/dsk/d7 # metaclear -f /dev/md/dsk/d17 # metaclear -f /dev/md/dsk/d27 for slice in 0 1 3 6 7 do metaclear -f /dev/md/dsk/d${slice} metaclear -f /dev/md/dsk/d1${slice} metaclear -f /dev/md/dsk/d2${slice} done cp /etc/vfstab.SVM.verybeginning /etc/vfstab cp /etc/system.SVM.verybeginning /etc/system fi echo Overriding defaults for sherlock SRCDISK=c0t0d0 DSTDISK=c0t2d0 METADEV=5 MIRRORSWAP=true EXCLUDE_MOUNT_POINTS="/export/home" ;; sherlock.oas.uci.edu) if metastat > /dev/null 2>&1 then echo Removing previous SVM setup metadb -d /dev/dsk/c0t0d0s6 metadb -f -d /dev/dsk/c0t2d0s6 metaclear -f /dev/md/dsk/d0 metaclear -f /dev/md/dsk/d10 metaclear -f /dev/md/dsk/d20 metaclear -f /dev/md/dsk/d3 metaclear -f /dev/md/dsk/d13 metaclear -f /dev/md/dsk/d23 metaclear -f /dev/md/dsk/d7 metaclear -f /dev/md/dsk/d17 metaclear -f /dev/md/dsk/d27 cp /etc/vfstab.SVM.verybeginning /etc/vfstab cp /etc/system.SVM.verybeginning /etc/system fi echo Overriding defaults for sherlock SRCDISK=c0t0d0 DSTDISK=c0t2d0 METADEV=6 MIRRORSWAP=true ;; bingy.nac.uci.edu) if metastat > /dev/null 2>&1 then echo Removing previous SVM setup set -x metadb -d /dev/dsk/c0t1d0s7 metadb -f -d /dev/dsk/c0t2d0s7 metaclear -f /dev/md/dsk/d0 metaclear -f /dev/md/dsk/d10 metaclear -f /dev/md/dsk/d20 cp /etc/vfstab.SVM.verybeginning /etc/vfstab cp /etc/system.SVM.verybeginning /etc/system set +x fi echo Overriding defaults for bingy SRCDISK=c0t1d0 DSTDISK=c0t2d0 METADEV=7 MIRRORSWAP=true SYSTEM_DISK=false ;; *) 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 is a digit # returns the digit mapped to the corresponding slice letter, for eeprom's # boot-device. IOW, 0 is a, 2 is c, and so on. function number_to_letter { echo "$1" | tr '[0-9]' '[a-j]' } # $1 source partition # returns corresponding dest partition function dst_from_src { s=s"$(get_slice $1)" echo "$DSTDISK""$s" } # useless use of cat, but oh well :) SRCPARTITIONS="$(cat /etc/vfstab | grep ^/dev/dsk/${SRCDISK}s | \ while read line do # need to be able to, for example, exclude /export/home based on this: # /dev/dsk/c0t0d0s7 /dev/rdsk/c0t0d0s7 /export/home ufs 2 yes - # # this sort of thing is guaranteed to # works in bash, and probably ksh, but some bourne shells will # choke on it - they will not be able to keep the value of found # when the loop terminates found=0 for mtpt in $EXCLUDE_MOUNT_POINTS do if [ $(echo $line | awk ' { print $3 } ') = $mtpt ] then found=1 break else : found stays 0 fi done if [ $found = 1 ] then echo Excluding line: $line 1>&2 else echo $line fi done | \ 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 'Sorry, no source partitions found. Do you have vfstab entries for your' echo 'source partitions? It may seem pointless, but it lets me know what' echo 'you need in terms of SVM.' exit 1 fi if [ "$SYSTEM_DISK" = true ] then SRCROOTPARTITION="$(mount | grep /${SRCDISK}s | \ awk ' { if ($1 == "/") print $3 }' | \ sed 's#/dev/dsk/##')" DSTROOTPARTITION="$(dst_from_src $SRCROOTPARTITION)" fi echo SRCDISK is "$SRCDISK" echo DSTDISK is "$DSTDISK" echo METADEV is "$METADEV" echo MIRRORSWAP is "$MIRRORSWAP" echo SYSTEM_DISK is "$SYSTEM_DISK" # we want this one rewritten to all one line... echo SRCPARTITIONS is $SRCPARTITIONS if [ "$SYSTEM_DISK" = true ] then echo SRCROOTPARTITION is "$SRCROOTPARTITION" echo DSTROOTPARTITION is "$DSTROOTPARTITION" fi # method by which to copy the partition table. s2 is the entire disk, by # convention, starting with the disk label. Use dup-label if available, # otherwise use dd method=1 if type dup-label > /dev/null 2>&1 then echo Good, found dup-label else echo Sorry, dup-label not found, so we will just use dd instead method=3 fi echo echo Hit interrupt now if you do not want these settings! echo Sleeping for 10 seconds... sleep 10 echo echo 'We begin! :)' echo # save a copy of the DSTDISK's previous partitioning, in case there's # lost disk space prtvtoc /dev/rdsk/"$DSTDISK"s2 > /var/tmp/${DSTDISK}s2 case "$method" in 1) # copy partition table. s2 is the entire disk, by convention, starting # with the disk label 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 ;; 2) # this complains about cylinder boundaries if your disks aren't the # same. prtvtoc /dev/rdsk/"$SRCDISK"s2 | fmthard -s - /dev/rdsk/"$DSTDISK"s2 ;; 3) # 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? dd if=/dev/dsk/"$SRCDISK"s2 of=/dev/dsk/"$DSTDISK"s2 count=16 ;; esac set -x # Set up metadb's. Writing this to an actual filesystem is a no-no if ! metadb -a -f -c2 "$SRCDISK"s$METADEV "$DSTDISK"s$METADEV then echo error "metadb'ing" "$SRCDISK"s$METADEV and "$DSTDISK"s$METADEV 1>&2 exit 1 fi set +x ( echo '#!/bin/sh' echo 'set -x' ) > /var/tmp/finish-up chmod 755 /var/tmp/finish-up # set up meta devices. # dx's are mirror devices # d1x's are source devices # d2x's are destination devices for srcpart in $SRCPARTITIONS do slice="$(get_slice $srcpart)" srcmetadev=d1"$slice" dstmetadev=d2"$slice" # need "force" -f flag for mounted partitions metainit -f "$srcmetadev" 1 1 /dev/dsk/"$srcpart" dstpart="$(dst_from_src $srcpart)" metainit "$dstmetadev" 1 1 /dev/dsk/"$dstpart" # set up mirror with source submirror metainit d"$slice" -m d1"$slice" echo metattach d"$slice" d2"$slice" >> /var/tmp/finish-up done # backup vfstab and system. 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 system 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 # Tell SVM to get root from the new root mirror. This also modifies # /etc/vfstab, but only for the root filesystem. if [ "$SYSTEM_DISK" = true ] then metaroot d"$(get_slice $SRCROOTPARTITION)" fi # 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 both disks. We intentionally do not mirror # them. $SRCDISK will most likely already have one or more # swap partitions. We set up identical partitions on the # $DSTDISK, 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 - echo "/dev/dsk/${DSTDISK}s$(get_slice $1) $2 $3 $4 $5 $6 $7" fi elif [ "$#" = 7 ] && \ echo "$1" | egrep "/dev/md/dsk/|/dev/md/rdsk" > /dev/null then # Echo any other md lines, because root should already have # been set up by metaroot. # Someday we may want to throw out everything but the root # filesystem though. 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 if [ "$SYSTEM_DISK" = true ] then if grep '^set md:mirrored_root_flag=1$' /etc/system > /dev/null then echo Not modifying /etc/system else echo Modifying /etc/system ( echo echo echo '# Tell SVM to ignore quorum for the mirrored root' echo 'set md:mirrored_root_flag=1' # this is useful because if you're mirroring two disks, and you # lose half your metadb's, there is no way to get a quorum if you # have an equal number of metadb's on each disk. 50% isn't a # majority. However, if this script is ever extended to handle # mirroring across 3 or more disks, then we can turn this off. ) >> /etc/system fi else echo This is not a system disk, I am not changing /etc/system fi if [ "$SYSTEM_DISK" = true ] then srccontroller="$(get_controller $SRCROOTPARTITION)" dstcontroller="$(get_controller $DSTROOTPARTITION)" case "$srccontroller.$dstcontroller" in 0.0) eeprom "boot-device=disk$(get_target $SRCROOTPARTITION):$(number_to_letter $(get_slice $SRCROOTPARTITION)) disk$(get_target $DSTROOTPARTITION):$(number_to_letter $(get_slice $DSTROOTPARTITION))" eeprom boot-device ;; *) echo "Sorry, this script cannot set up your boot device for disks" echo "that are not on controller 0" ;; esac fi # 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 # this appeases metachk metastat -p > /etc/lvm/md.tab 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 "Setup is -NOT- complete. You only have half of your mirror disks" echo "added at this point. After you reboot, you need to run" echo "/var/adm/finish-up. This should complete the configuration of SVM" if [ "$SYSTEM_DISK" = true ] then echo "for root disk mirroring." else echo "for data disk mirroring." fi echo echo "A copy of your DSTDISK's previous partitioning is in" echo "/var/tmp/${DSTDISK}s2". If your DSTDISK is larger than your echo "SRCDISK, you may be able to recover some (unmirrored) disk space" echo "on $DSTDISK with some fancy partitioning, but that's beyond the" echo "scope of this program, at least at this time."