#!/bin/bash # To move a machine onto a new ZFS # Load some settings . /etc/default/zfs-backup # To move a machine from one zpool to another function dosql_local() { SQL="$1" psql -q -A -t -c "$SQL" } function dosql_remote() { SQL="$1" cat <<EOF | ssh -A root@$NEW_SERVER sudo -u postgres psql -q -A backups -t $SQL EOF } function cmd_remote() { CMD="$1" ssh root@$NEW_SERVER $CMD } function create_host_record() { FQDN=$1 # NB we start with offline=t,disabled=t whilst we migrate the backup. HOSTID=`dosql_remote " insert into host (hostname, offline, disabled) values ('$FQDN',true,true); select host_id from host where hostname='$FQDN';"` echo $HOSTID } function backuptask() { HOSTID=$1 TASKNAME="$2" TARGET=$3 SOURCEDIR=$4 SOFT=$5 HARD=$5 if [ -z "$SOFT" ]; then SOFT=14400 fi if [ -z "$HARD" ]; then HARD=86400 fi BTID=`dosql_remote " insert into backup_task(host_id,backup_task_name,timeout_soft,timeout_hard,zfs_target,backup_type_id) values ($HOSTID,'$TASKNAME',$SOFT,$HARD,'$TARGET',3); insert into zfs_rsync_detail (backup_task_id,directory_source) values ((select backup_task_id from backup_task where zfs_target='$TARGET' and host_id=$HOSTID),'$SOURCEDIR'); select backup_task_id from backup_task natural join host natural join zfs_rsync_detail where hostname='$FQDN' and directory_source='$SOURCEDIR' and backup_type_id=3;"` echo $BTID } function configure_pruning() { CURRENT_TASK_ID=$1 NEW_TASK_ID=$2 PRUNING_IDS=$(dosql_local "select zfs_pruning_id from zfs_pruning where backup_task_id=$CURRENT_TASK_ID") for PRUNING_ID in $PRUNING_IDS; do PRUNING_AGE=$(dosql_local "select zfs_pruning_age from zfs_pruning where zfs_pruning_id=$PRUNING_ID") PRUNING_COUNT=$(dosql_local "select zfs_pruning_count from zfs_pruning where zfs_pruning_id=$PRUNING_ID") FLAGNAME=$(dosql_local "select flagname from zfs_pruning where zfs_pruning_id=$PRUNING_ID") dosql_remote "insert into zfs_pruning (backup_task_id, zfs_pruning_age,zfs_pruning_count,flagname) values ($NEW_TASK_ID, $PRUNING_AGE, $PRUNING_COUNT, '$FLAGNAME')" done } if [ $# -lt 2 ] ; then echo Usage: $0 backup_target_fqdn new_backup_server [zpool_name] echo Moves the named machine to a new server exit 1 fi HN=$1 # Find FQDN for $HN FQDN=`getent hosts $HN | awk ' { print $2 } '` NEW_SERVER=$2 ZPOOL_TARGET=$3 # Which zfs is it already on? echo Finding source ZFS for $FQDN SOURCES=`zfs list -H -o name | grep -v zroot | grep /$FQDN$ | sed "sZ/'$FQDN$'ZZ" | wc -l ` #echo Found $SOURCES possibilities if [ $SOURCES != 1 ] ;then echo Found more than one possible ZFS for $HN exit 2 fi if ! grep -q ^$NEW_SERVER: $PGPASSFILE ; then echo $NEW_SERVER not found in $PGPASSFILE \(use fqdn?\) exit 5 fi SOURCE=`zfs list -H -o name | grep -v zroot | grep /$FQDN$ | sed "sZ/$FQDN\\$ZZ"` # Which target is it? if [ -z "$ZPOOL_TARGET" ] ; then echo Finding a target ZFS automatically ZPOOL=$(cmd_remote "zfs list -H -oname -S avail -d0 | grep -v zroot | head -n1") ZPOOL_TARGET=$ZPOOL fi if ! ssh root@$NEW_SERVER zpool list $ZPOOL_TARGET &>/dev/null ; then echo $ZPOOL_TARGET does not exist on $NEW_SERVER exit 3 fi echo moving $FQDN from $SOURCE to $ZPOOL_TARGET on $NEW_SERVER set -e RUNNING=`dosql_local "select count(*) from backup_log natural join backup_task natural join host where hostname='$FQDN' and ended_processing is null and started_processing is not null"` RUNNING=`echo $RUNNING` # chomp #echo RUNNING $RUNNING if ! [ "$RUNNING" = 0 ] ; then echo $FQDN has a job running exit 3 fi # Disable this host locally dosql_local "update host set disabled='t' where hostname='$FQDN';" # De-queue jobs for this host dosql_local "delete from backup_log where backup_task_id in (select backup_task_id from backup_task natural join host where hostname='$FQDN') and ended_processing is null and started_processing is null;" # Move ZFS NOW=$(date +%s) zfs snapshot -r $SOURCE/$FQDN@$NOW zfs hold zfs-moving $SOURCE/$FQDN@$NOW SIZE=`zfs list -H -o used $SOURCE/$FQDN` echo Sending ZFS [ $SIZE] zfs send -R $SOURCE/$FQDN@$NOW | mbuffer | ssh root@$NEW_SERVER zfs receive $ZPOOL_TARGET/$FQDN CURRENT_HOST_ID=$(dosql_local "select host_id from host where hostname='$FQDN'") NEW_HOST_ID=$(create_host_record $FQDN) # get current tasks TASK_IDS=$(dosql_local "select backup_task_id from backup_task where host_id='$CURRENT_HOST_ID'") # create new tasks for TASK_ID in $TASK_IDS; do echo got task $TASK_ID SOURCEDIR=$(dosql_local "select directory_source from zfs_rsync_detail where backup_task_id='$TASK_ID'") SOFT=$(dosql_local "select timeout_soft from backup_task where backup_task_id='$TASK_ID'") HARD=$(dosql_local "select timeout_hard from backup_task where backup_task_id='$TASK_ID'") TASKNAME=$(dosql_local "select backup_task_name from backup_task where backup_task_id='$TASK_ID'") CURRENT_ZFS_TARGET=$(dosql_local "select zfs_target from backup_task where backup_task_id='$TASK_ID'") NEW_ZFS_TARGET=${ZPOOL_TARGET}/$(echo $CURRENT_ZFS_TARGET | cut -d '/' -f 2-) NEW_TASK_ID=$(backuptask $NEW_HOST_ID "$TASKNAME" $NEW_ZFS_TARGET $SOURCEDIR $SOFT $HARD) configure_pruning $TASK_ID $NEW_TASK_ID scp /etc/chem-zfs-backup-server/zfs-rsync.d/${FQDN}_${SOURCEDIR//\//.} $NEW_SERVER:/etc/chem-zfs-backup-server/zfs-rsync.d rsync -av /etc/chem-zfs-backup-server/zfs-rsync.d/${FQDN} $NEW_SERVER:/etc/chem-zfs-backup-server/zfs-rsync.d done dosql_remote "update host set disabled='f',offline='f' where host_id=$NEW_HOST_ID"