#!/bin/bash

#====================================================
# 2016 Dave Bechtel
#====================================================

# Interactive demo: make a dynamic-growing zfs mirror pool from 1 disk to 6
# zfs pools are on loop files (virtual disks)
# NOTE this demo waits for multiple press-a-key-to-proceed prompts

# trace on: set -x // off: set +x  # REF: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html
# PROTIP: Avoid having to turn off and avoid clutter by using subshell 
# REF: http://serverfault.com/questions/16204/how-to-make-bash-scripts-print-out-every-command-before-it-executes

# TODO setme or pass "new" as arg1
newdisks=1  # set 1 to zero-out new disk files

# DONE redirect to non-ZFS spinning disk if avail, otherwise use RAMdisk
zdpath="/run/shm/zdisks"

# if milterausb3 is mounted, use it
usemil=$(df |grep -c /mnt/milterausb3)
[ $usemil -gt 0 ] && zdpath="/mnt/milterausb3/zdisks"
# xxx TODO EDITME

mkdir -pv $zdpath
ln $zdpath /zdisks -sfn
cd /zdisks || exit 99

DS=400  # Disksize in MB
[ $usemil -gt 0 ] && DS=465 

let mkpop=$DS-100  # size of populate-data file in MB

# NOTE we need more loop devices; 1-time fix REF: http://askubuntu.com/questions/499131/how-to-use-more-than-255-loop-devices

logfile=~/mkdynamic-grow-pool.log
> $logfile # clearit

function nomorepool () {
 zfs umount -f $1 2>> $logfile

# zpool export $1 2>> $logfile

 zpool status $1
 zpool destroy -f $1 2>> $logfile
 zpool status -L -P -x
}

pool1="zdynpool1"

nomorepool $pool1

df -hT
zpool status $pool1
echo "POOL $pool1 SHOULD BE GONE -PK"

# if not exist, trip
[ -e zdyndisk1 ] || let newdisks=1
[ "$1" = "new" ] && let newdisks=1  # if arg passed

[ $newdisks -gt 0 ] && read -n 1
[ $newdisks -gt 0 ] && clearcache.sh

# getting rid of sync for each dd should speed things up
echo "Preparing virtual disks... (Size: $DS MB)"
for i in {1..8};do
  printf $i...

# NECESSARY if re-using disks that were previously in a pool!!
  zpool labelclear -f /zdisks/zdyndisk$i
  
[ $newdisks -gt 0 ] && time dd if=/dev/zero of=zdyndisk$i bs=1M count=$DS 2>&1 |egrep 'copied|real' >> $logfile

done

echo 'Syncing...'
time sync
# NOTE may not want to do this if you have a long-running I/O process (tar) because the sync will hang here

ls -alh |tee -a $logfile

#NOTE: ' zfs add ' = add a disk (expand, non-redundant); ' zfs attach ' = add mirror

# REF: http://zfsonlinux.org/faq.html#WhatDevNamesShouldIUseWhenCreatingMyPool
# REF: https://flux.org.uk/tech/2007/03/zfs_tutorial_1.html


############ create 1-disk NORAID
(set -x
 time zpool create -f -o ashift=12 -o autoexpand=on -O atime=off $pool1 \
   /zdisks/zdyndisk1) #; set +x

echo 'o Populating pool with random,uncompressible data...'
# if file not there, create
[ -e /root/tmpfile ] && [ $newdisks -eq 0 ] || time dd if=/dev/urandom of=/root/tmpfile bs=1M count=$mkpop
  time cp -v /root/tmpfile /$pool1

echo 'o Should now have a 1-disk, non-redundant pool with some data in it:'
zpool status $pool1; df -h /$pool1
echo '';printf 'PK to add mirror to single disk:';read -n 1


########### add mirror to 1-disk
# REF: http://docs.oracle.com/cd/E19253-01/819-5461/6n7ht6qvl/index.html
(set -x
 time zpool attach $pool1 \
  /zdisks/zdyndisk1 /zdisks/zdyndisk2) 
  
echo 'o Should now have a 2-disk, MIRRORED pool with RAID1:'
zpool status $pool1; df -hT /$pool1
echo '';echo 'o NOTE that available pool space has not changed yet - we have only added Redundancy'
echo '! NORMALLY we would wait for the resilver to complete before proceeding! # zpool status  # until resilvered'
printf 'PK to add another set of mirrored disks to the existing pool:';read -n 1
  
  
########### add 2-disk mirror to 2-disk
# REF: http://docs.oracle.com/cd/E19253-01/819-5461/6n7ht6qvk/index.html
(set -x
 time zpool add -o ashift=12 $pool1 \
   mirror /zdisks/zdyndisk3 /zdisks/zdyndisk4)

echo 'o Populating pool with more data...'
time cp -v /$pool1/tmpfile /$pool1/tmpfile2    
    
echo 'o Should now have a 4-disk, redundant pool with RAID10:'
zpool status $pool1; df -hT /$pool1
ls -lh /$pool1/*

echo '';echo 'o NOTE that the available pool space should be approximately 2x what we had before, minus a bit of overhead'
echo '! Again - NORMALLY we would wait for the resilver to complete before proceeding! # zpool status  # until resilvered'
# DONE adapt scrubwatch to wait4resilver
printf 'PK to add final 2-disk mirror with 1 hotspare:';read -n 1


########### add 2-disk mirror to 4-disk, with 1 spare
# REF: http://docs.oracle.com/cd/E19253-01/819-5461/6n7ht6qvk/index.html
(set -x
 time zpool add -o ashift=12 $pool1 \
   mirror /zdisks/zdyndisk5 /zdisks/zdyndisk6 \
   spare /zdisks/zdyndisk7)


echo 'o Should now have a 6-disk, highly failure-resistant pool with RAID10:'
zpool status $pool1; df -hT /$pool1; zpool list $pool1
echo ''
echo 'o NOTE that the available pool space should be approximately 1.5x what we had with the 4-disk mirror set,'
echo ' minus a bit of overhead'
echo '! Again - NORMALLY we would wait for the resilver to complete before proceeding! # zpool status  # until resilvered'
printf 'PK to proceed with next phase (POOL WILL BE DESTROYED and rebuilt):';read -n 1



####################### start over and build redundant RAID10 pool from 2-disk RAID0

nomorepool $pool1

df -h
zpool status $pool1
echo "POOL $pool1 SHOULD BE GONE -PK";read -n 1


############ create 2-disk NORAID with max available space
(set -x
 zpool create -f -o ashift=12 -o autoexpand=on -O atime=off $pool1 \
   /zdisks/zdyndisk1 /zdisks/zdyndisk2)
  
echo 'o Populating pool with data...'
time cp -v /root/tmpfile /$pool1    
time cp -v /$pool1/tmpfile /$pool1/tmpfile2  
  
echo 'o OK, say our client has a limited budget and wants to start with 2 disks with maximum writable space, NO redundancy... (RAID0)'
zpool status $pool1; df -hT /$pool1
echo '';echo 'o NOTE that the available pool space should be about the capacity of disk1+disk2 minus a bit of overhead;'
echo '+ it will be fast, but vulnerable to failure. NOTE that we will have done a *full burn-in test* on the drives 1st!'
echo 'o After a couple of weeks, client is able to buy 2 more drives -- we can add redundancy!'
printf 'PK to add a mirror to 1st disk:';read -n 1


########### add a mirror OTF to 1st disk of RAID0, 1st half
# REF: http://docs.oracle.com/cd/E19253-01/819-5461/6n7ht6qvl/index.html
(set -x
 time zpool attach $pool1 \
   /zdisks/zdyndisk1 /zdisks/zdyndisk3)

echo 'o Should now have a 3-disk, UNBALANCED MIRRORED pool with half-RAID1:'
zpool status $pool1; df -hT /$pool1
echo '';echo 'o NOTE that available pool space has **not changed** - we have only added Redundancy to HALF of the pool!'
echo '! NORMALLY we would NEED to wait for the resilver to complete before proceeding! # zpool status  # until resilvered'
printf 'PK to add a mirror to the 2nd half (will end up being RAID10):';read -n 1

  
########### add mirror OTF to 2nd disk of original RAID0
# REF: http://docs.oracle.com/cd/E19253-01/819-5461/6n7ht6qvl/index.html
(set -x
 time zpool attach $pool1 \
   /zdisks/zdyndisk2 /zdisks/zdyndisk4)

echo 'o Populating pool with more data...'
time cp -v /$pool1/tmpfile /$pool1/tmpfile2    
  
echo 'o Should now have a 4-disk, redundant pool with RAID10 - but we built it differently since it started out as a RAID0:'
zpool status $pool1; df -hT /$pool1

echo '';echo 'o NOTE that the available pool space is still the same as what we started with,'
echo '+ only now the pool has been upgraded in-place to be failure-resistant - with no downtime!'
echo '! Again - NORMALLY we would wait for the resilver to complete before proceeding! # zpool status  # until resilvered'
printf 'PK to expand this now-RAID10 pool with the final 2-disk mirror, plus 1 hotspare:';read -n 1


########### Expand free space = add final 2-disk mirror to 4-disk RAID10, with 1 spare
# REF: http://docs.oracle.com/cd/E19253-01/819-5461/6n7ht6qvk/index.html
(set -x
 time zpool add -o ashift=12 $pool1 \
   mirror /zdisks/zdyndisk5 /zdisks/zdyndisk6 \
   spare /zdisks/zdyndisk7)

echo ''
echo 'o We should now have ended up with a 6-disk, mirrored, failure-resistant pool with RAID10,'
echo ' with some acceptable risks but built on a budget:'
      
    
###########
# ENDIT
zpool status -L $pool1
#df -h |head -n 1 # unnec; fortuitously, grep also grabs "size"
df -hT |grep z 	# /$pool1 /$pool2 /$pool3a /$pool3b /$pool3c
(set -x; zfs list $pool1; zpool list $pool1)
echo ''
echo "Log: $logfile"

/bin/rm -v /zdisks/tmpfile

echo "! CLEANUP: PK to Destroy example pool $pool1 or ^C to keep it"; read -n 1
nomorepool $pool1
zpool status

exit;

====================================================
# 2016 Dave Bechtel
====================================================

# df -h /zdynpool1
Filesystem      Size  Used Avail Use% Mounted on
zdynpool1       832M     0  832M   0% /zdynpool1

# zpool status
  pool: zdynpool1
 state: ONLINE
  scan: resilvered 276K in 0h0m with 0 errors on Mon May  9 16:49:45 2016
config:
        NAME                   STATE     READ WRITE CKSUM
        zdynpool1              ONLINE       0     0     0
          mirror-0             ONLINE       0     0     0
            /zdisks/zdyndisk1  ONLINE       0     0     0
            /zdisks/zdyndisk2  ONLINE       0     0     0
          mirror-1             ONLINE       0     0     0
            /zdisks/zdyndisk3  ONLINE       0     0     0
            /zdisks/zdyndisk4  ONLINE       0     0     0
          mirror-2             ONLINE       0     0     0
            /zdisks/zdyndisk5  ONLINE       0     0     0
            /zdisks/zdyndisk6  ONLINE       0     0     0
        spares
          /zdisks/zdyndisk7    AVAIL
errors: No known data errors

========================================================

2016.0525 moving /zdisks to /run/shm due to usb3 thumbdrive heat / slowdowns
+ added "use milterausb3" capability, with bigger disk sizes

2016.0610 added "ashift=12" to zpool ADD commands (zfs quirk, ashift is not a global pool inherited property)
