#!/bin/sh

# mkfailover

USAGE=\
"$0: copy this host (HOST1) to a backup machine (HOST2) for quick failover
Options: 
  -b, --back       copy / from HOST2 to HOST1's sys.mirror partition
  -H, --home       copy /home from HOST1 to HOST2's home.mirror partition (requires --sync)
  -n, --name NAME  set the name of HOST2 (default: HOST1's name with '2' appended)
  -S, --stop       stop processes on HOST1 that might alter disk contents
  -s, --sync       copy / from HOST1 to HOST2's sys.mirror partition
  -t, --test       make HOST2 retain its name and IP number for testing
  -h, --help       display this help and exit
Prerequisites:
  In order to use this script you need
  - Two machines named '<host>' (local) and '<host>2' (remote), where <host> can be any name.
    Use --name to set the remote host's name to something other than the default.
  - A partition on the remote host, mounted as '/sys.mirror', with enough space
    to take a copy of the local host's system partition.
  - If you have a separate /home partition on the local host, the remote host needs to have
    a partition mounted as '/home.mirror'.
Examples:
  $0 --sync

     copies the system partition of this host to the sys.mirror partition
     of a host with the same name as this one, but with '2' appended, and
     makes it bootable.

  $0 --sync --home

     same as above, but also copies this hosts's /home partition to the
     remote host's /home.mirror partition.

  $0 --sync --home --back

     as above, but also copies the remote host's system partition to this
     host's sys.mirror partition. This can be used to have the two hosts
     swap their identities.

  $0 --sync --test

     the additional --test makes the remote host retain its name and IP
     number, so that it can be booted for a test drive without interfering
     with this host.

  $0 --sync --home --back --stop

     do this if you intend to shutdown the local host and reboot the remote
     host with the local hosts identity.
"

# Helper:

function Help { printf "$USAGE"; exit 0; }
function Fail { echo "$1"; exit 1; }
function GetIP { IP=`host $1` || Fail "no ip found for host $1"; echo "$IP" | sed 's/.* //'; }
function Check { P=`$1 $2 df | grep " $3$"`; [[ "$P" != "" ]] || Fail "$2:$3 not found"; }

# Options:

[[ "$1" != "" ]] || Help

OPT_BACK=""
OPT_HOME=""
OPT_NAME=""
OPT_STOP=""
OPT_SYNC=""
OPT_TEST=""

while [[ "$1" ]]; do
      case "$1" in
           -b | --back) OPT_BACK="yes"
                        ;;
           -H | --home) OPT_HOME="yes"
                        ;;
           -n | --name) shift
                        OPT_NAME="$1"
                        [[ "$OPT_NAME" != "" ]] || Fail "missing host name"
                        ;;
           -S | --stop) OPT_STOP="yes"
                        ;;
           -s | --sync) OPT_SYNC="yes"
                        ;;
           -t | --test) OPT_TEST="yes"
                        ;;
           -h | --help) Help
                        ;;
           *)           Fail "unknown option: '$1'"
                        ;;
           esac
      shift
      done

[[ "$OPT_HOME" != "" ]] && [[ "$OPT_SYNC" == "" ]] && Fail "--home requires --sync"

# Setup:

RSYNC="/usr/bin/rsync -aHSx --numeric-ids --delete --force"

HOST1=`hostname`
HOST2="${HOST1}2"
[[ "$OPT_NAME" == "" ]] || HOST2="$OPT_NAME"

IP1=`GetIP $HOST1`
IP2=`GetIP $HOST2`

SYSMIRROR="/sys.mirror"
HOMEMIRROR="/home.mirror"

[[ "$OPT_SYNC" != "" ]] && Check ssh $HOST2 $SYSMIRROR
[[ "$OPT_TEST" != "" ]] && Check ssh $HOST2 $SYSMIRROR
[[ "$OPT_BACK" != "" ]] && Check "" ""      $SYSMIRROR
[[ "$OPT_HOME" != "" ]] && Check ssh $HOST2 $HOMEMIRROR

function Reconfigure
{
  echo \
  "
  mount -B $SYSMIRROR /mnt;
  mkdir -p /mnt/{dev,proc,sys,run};
  mount -B /sys /mnt/sys;
  mount -B /dev /mnt/dev;
  mount -B /run /mnt/run;
  mount -B /proc /mnt/proc;
  cp /etc/udev/rules.d/70-persistent-net.rules /mnt/etc/udev/rules.d;
  chroot /mnt mkinitrd;
  chroot /mnt update-bootloader;
  mount --make-rprivate /mnt;
  umount /mnt/{dev,proc,sys,run};
  umount /mnt;
  "
}

# Stop processes on HOST1:

if [[ "$OPT_STOP" == "yes" ]]; then
   rccron stop
   rcsendmail stop
   fi

# Sync HOST1 to HOST2:

if [[ "$OPT_SYNC" == "yes" ]]; then
   echo "syncing $HOST1:/ to $HOST2:$SYSMIRROR..."
   $RSYNC /       $HOST2:$SYSMIRROR
   echo "done"
   echo "syncing $HOST1:/home/ to $HOST2:$HOMEMIRROR..."
   [[ "$OPT_HOME" == "yes" ]] && $RSYNC /home/  $HOST2:$HOMEMIRROR
   echo "done"
   echo "reconfiguring $HOST2:$SYSMIRROR..."
   ssh $HOST2 `Reconfigure`
   echo "done"
   fi

# Sync HOST2 to HOST1:

if [[ "$OPT_BACK" == "yes" ]]; then
   echo "syncing $HOST2:/ to $HOST1:$SYSMIRROR..."
   $RSYNC $HOST2:/  $SYSMIRROR
   echo "reconfiguring $HOST1:$SYSMIRROR..."
   eval `Reconfigure`
   echo "done"
   fi

# Prepare HOST2 for test drive:

if [[ "$OPT_TEST" == "yes" ]]; then
   echo "preparing $HOST2 for test drive..."
   ssh $HOST2 "sed -i -e 's/$IP1/$IP2/' $SYSMIRROR/etc/sysconfig/network/ifcfg-eth0"
   ssh $HOST2 "echo $HOST2 > $SYSMIRROR/etc/hostname"
   ssh $HOST2 "cp /etc/crontab $SYSMIRROR/etc"
   echo "done"
   echo "TODO: reboot $HOST2 from $SYSMIRROR"
   echo "TODO: check if it looks like $HOST1"
   echo "TODO: reboot $HOST2 as $HOST2!!"
   exit
   fi
