better dry-run handling, separate "--copy" option.

This commit is contained in:
2021-04-28 14:55:50 +02:00
parent 9b3fb864cd
commit edbdab3a20

View File

@@ -20,7 +20,7 @@
# #
# DESCRIPTION # DESCRIPTION
# Duplicate SRC disk partitions to same structured DST disk ones. # Duplicate SRC disk partitions to same structured DST disk ones.
# if SRC is omitted, tue running system disk (where root partition # if SRC is omitted, the running system disk (where root partition
# resides) will be used. # resides) will be used.
# Both SRC and DST *must* have same partition base LABELs - as 'LABEL' # Both SRC and DST *must* have same partition base LABELs - as 'LABEL'
# field for lsblk(1) and blkid(1), with an ending character (unique per # field for lsblk(1) and blkid(1), with an ending character (unique per
@@ -31,8 +31,17 @@
# must be 'root2', 'export2, and 'swap2'. # must be 'root2', 'export2, and 'swap2'.
# #
# OPTIONS # OPTIONS
# -d, -n, --dry-run, --no # -a, --autofs=DIR
# Dry-run: nothing will be written to disk. # Use DIR as autofs "LABEL-based" automount. See AUTOFS below. Default
# is /mnt.
#
# -c, --copy=ACTION
# ACTION can be 'yes' (all eligible partitions will be copied), 'no'
# (no partition will be copied), or 'ask' (will ask for all eligible
# partitions). Default is 'no'.
#
# -d, --dry-run
# Dry-run: nothing will be really be done.
# #
# -g, --grub # -g, --grub
# Install grub on destination disk. # Install grub on destination disk.
@@ -52,10 +61,6 @@
# Mandatory if SRC is provided, forbidden otherwise. # Mandatory if SRC is provided, forbidden otherwise.
# PARTNUM is root partition number on SRC disk. # PARTNUM is root partition number on SRC disk.
# #
# -y, --yes
# Do not ask for actions confirmation. Default is to display next
# action and ask user to [y] do it, [q] quit, [s] skip.
#
# EXAMPLES # EXAMPLES
# Copy sda to sdb, root partition is partition (sda1/sdb1) # Copy sda to sdb, root partition is partition (sda1/sdb1)
# $ sudo dup-live-disk.sh --root 1 sda sdb # $ sudo dup-live-disk.sh --root 1 sda sdb
@@ -92,16 +97,17 @@ Usage: $CMD [OPTIONS] [SRC] DST
Duplicate SRC (or live system) disk partitions to DST disk partitions. Duplicate SRC (or live system) disk partitions to DST disk partitions.
Options: Options:
-d, -n, --dry-run, --no dry-run: nothing will be written to disk -a, --autofs=DIR autofs "LABEL-based" directory. Default is '/mnt'.
-g, --grub install grub on destination disk -c, --copy=ACTION do partitions copies ('yes'), do not copy then ('no')
-h, --help this help or ask for each of them ('ask'). Default is 'no'.
-m, --man display a "man-like" page and exit -d, --dry-run dry-run: nothing will be written to disk
--mariadb stop and restart mysql/mariadb server before and -g, --grub install grub on destination disk
after copies -h, --help this help
-r, --root=PARTNUM root partition number on SRC device -m, --man display a "man-like" page and exit
mandatory if and only if SRC is provided --mariadb stop and restart mysql/mariadb server before and
-y, --yes DANGER ! perform all actions without user after copies
confirmation -r, --root=PARTNUM root partition number on SRC device
mandatory if and only if SRC is provided
SRC and DST have strong constraints on partitions schemes and naming. SRC and DST have strong constraints on partitions schemes and naming.
Type '$CMD --man" for more details" Type '$CMD --man" for more details"
@@ -112,19 +118,19 @@ _EOF
# mariadb start/stop # mariadb start/stop
function mariadb_maybe_stop { function mariadb_maybe_stop {
if [[ $MARIADB == yes ]] && systemctl is-active --quiet mysql; then if [[ $MARIADB == yes ]] && systemctl is-active --quiet mysql; then
log -n "stopping mariadb/mysql... " #log -n "stopping mariadb/mysql... "
systemctl stop mariadb echorun systemctl stop mariadb
# bug if script stops here # bug if script stops here
MARIADBSTOPPED=yes MARIADBSTOPPED=yes
log "done." #log "done."
fi fi
} }
function mariadb_maybe_start { function mariadb_maybe_start {
if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then
log -n "restarting mariadb/mysql... " #log -n "restarting mariadb/mysql... "
systemctl start mariadb echorun systemctl start mariadb
MARIADBSTOPPED=no MARIADBSTOPPED=no
log "done." #log "done."
fi fi
} }
@@ -155,9 +161,12 @@ function log {
# prints out and run a command. # prints out and run a command.
function echorun { function echorun {
log "%s" "$@" if [[ "$DRYRUN" == 'yes' ]]; then
"$@" log "%s " "dry-run: " "$@"
return $? else
log "%s " "$@"
"$@"
fi
} }
function error_handler { function error_handler {
@@ -168,15 +177,19 @@ function error_handler {
trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM
function exit_handler { function exit_handler {
local mnt
log "exit handler (at line $1)" log "exit handler (at line $1)"
mariadb_maybe_start mariadb_maybe_start
if [[ -v DSTMNT ]]; then if [[ -n "$DSTMNT" ]] && mountpoint -q "$DSTMNT"; then
umount "$DSTMNT/dev" for mnt in "$DSTMNT"/{dev,proc,sys}; do
umount "$DSTMNT/proc" if mountpoint -q "$mnt"; then
umount "$DSTMNT/sys" echorun umount "$mnt"
fi
done
fi fi
} }
trap 'exit_handler $LINENO' EXIT trap 'exit_handler $LINENO' EXIT
function check_block_device { function check_block_device {
@@ -237,14 +250,16 @@ SRC=""
DST="" DST=""
SRCROOT="" SRCROOT=""
ROOTPARTNUM="" ROOTPARTNUM=""
DOIT=manual
MARIADB=no DRYRUN=no # dry-run
MARIADBSTOPPED=no GRUB=no # install grub
GRUBINSTALL=no COPY=no # do FS copies
MARIADB=no # stop/start mysql/mariadb
MARIADBSTOPPED=no # mysql stopped ?
# short and long options # short and long options
SOPTS="dnghmr:y" SOPTS="c:dghmr:"
LOPTS="dry-run,no,grub,help,man,mariadb,root:,yes" LOPTS="copy:,dry-run,grub,help,man,mariadb,root:"
if ! TMP=$(getopt -o "$SOPTS" -l "$LOPTS" -n "$CMD" -- "$@"); then if ! TMP=$(getopt -o "$SOPTS" -l "$LOPTS" -n "$CMD" -- "$@"); then
log "Use '$CMD --help' or '$CMD --man' for help." log "Use '$CMD --help' or '$CMD --man' for help."
@@ -256,14 +271,22 @@ unset TMP
while true; do while true; do
case "$1" in case "$1" in
'-d'|'-n'|'--dry-run'|'--no') '-c'|'--copy')
DOIT=no case "$2" in
"no") COPY=no;;
"yes") COPY=yes;;
"ask") COPY=ask;;
*) log "invalid '$2' --copy flag"
usage
exit 1
esac
shift shift
continue ;;
'-d'|'--dry-run')
DRYRUN=yes
;; ;;
'-g'|'--grub') '-g'|'--grub')
GRUBINSTALL=yes GRUB=yes
shift
;; ;;
'-h'|'--help') '-h'|'--help')
usage usage
@@ -275,7 +298,6 @@ while true; do
;; ;;
'--mariadb') '--mariadb')
MARIADB=yes MARIADB=yes
shift
;; ;;
'-r'|'--root') '-r'|'--root')
ROOTPARTNUM="$2" ROOTPARTNUM="$2"
@@ -283,13 +305,7 @@ while true; do
log "$CMD: $ROOTPARTNUM must be a partition number." log "$CMD: $ROOTPARTNUM must be a partition number."
exit 1 exit 1
fi fi
shift 2
continue
;;
'-y'|'--yes')
DOIT=yes
shift shift
continue
;; ;;
'--') '--')
shift shift
@@ -301,9 +317,9 @@ while true; do
exit 1 exit 1
;; ;;
esac esac
shift
done done
case "$#" in case "$#" in
1) 1)
if [[ -n "$ROOTPARTNUM" ]]; then if [[ -n "$ROOTPARTNUM" ]]; then
@@ -355,7 +371,7 @@ declare -a LABELS=(${SRCLABELS[@]%?})
#log "LABELS=${#LABELS[@]} - ${LABELS[*]}" #log "LABELS=${#LABELS[@]} - ${LABELS[*]}"
declare -a SRCDEVS SRCFS SRCDOIT declare -a SRCDEVS SRCFS SRC_VALID_FS
# ... and corresponding partition device and fstype # ... and corresponding partition device and fstype
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
TMP="${LABELS[$i]}$SRCCHAR" TMP="${LABELS[$i]}$SRCCHAR"
@@ -365,8 +381,8 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
log "found LABEL=$TMP DEV=$TMPDEV FSTYPE=$TMPFS" log "found LABEL=$TMP DEV=$TMPDEV FSTYPE=$TMPFS"
SRCDEVS[$i]="$TMPDEV" SRCDEVS[$i]="$TMPDEV"
SRCFS[$i]="$TMPFS" SRCFS[$i]="$TMPFS"
SRCDOIT[$i]=n SRC_VALID_FS[$i]=n
in_array "$TMPFS" VALIDFS && SRCDOIT[$i]=y in_array "$TMPFS" VALIDFS && SRC_VALID_FS[$i]=y
unset TMP TMPDEV TMPFS unset TMP TMPDEV TMPFS
done done
@@ -386,9 +402,8 @@ fi
# log "ROOTLABEL=$ROOTLABEL" # log "ROOTLABEL=$ROOTLABEL"
# log "SRCROOTLABEL=%s DSTROOTLABEL=%s" "$SRCROOTLABEL" "$DSTROOTLABEL" # log "SRCROOTLABEL=%s DSTROOTLABEL=%s" "$SRCROOTLABEL" "$DSTROOTLABEL"
# log "SRCCHAR=%s DSTCHAR=%s" "$SRCCHAR" "$DSTCHAR" # log "SRCCHAR=%s DSTCHAR=%s" "$SRCCHAR" "$DSTCHAR"
# log "DOIT=%s\n" "$DOIT"
declare -a DSTLABELS DSTDEVS DSTFS DSTDOIT declare -a DSTLABELS DSTDEVS DSTFS DST_VALID_FS
# Do the same for correponding DST partitions labels, device, and fstype # Do the same for correponding DST partitions labels, device, and fstype
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
TMP="${LABELS[$i]}$DSTCHAR" TMP="${LABELS[$i]}$DSTCHAR"
@@ -408,8 +423,8 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
DSTLABELS[$i]="$TMP" DSTLABELS[$i]="$TMP"
DSTDEVS[$i]="$TMPDEV" DSTDEVS[$i]="$TMPDEV"
DSTFS[$i]="$TMPFS" DSTFS[$i]="$TMPFS"
DSTDOIT[$i]=n DST_VALID_FS[$i]=n
in_array "$TMPFS" VALIDFS && DSTDOIT[$i]=y in_array "$TMPFS" VALIDFS && DST_VALID_FS[$i]=y
unset TMP TMPDEV TMPFS unset TMP TMPDEV TMPFS
done done
@@ -417,56 +432,47 @@ for ((i=0; i<${#LABELS[@]}; ++i)); do
log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}" log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}"
log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}" log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}"
log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}" log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}"
log -n "%s %s " "${SRCDOIT[$i]}" "${DSTDOIT[$i]}" log -n "%s %s " "${SRC_VALID_FS[$i]}" "${DST_VALID_FS[$i]}"
[[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*" [[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*"
echo echo
done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SDOIT,DDOIT,ROOT -t -o " | " done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SVALID\?,DVALID\?,ROOT -t -o " | "
RSYNCOPTS="-axH --delete --delete-excluded" RSYNCOPTS="-axH --delete --delete-excluded"
FILTER=--filter="dir-merge .rsync-disk-copy" FILTER=--filter="dir-merge .rsync-disk-copy"
# copy loop # copy loop
for ((i=0; i<${#LABELS[@]}; ++i)); do for ((i=0; i<${#LABELS[@]}; ++i)); do
if [[ "${SRCDOIT[$i]}" != y ]] || [[ "${DSTDOIT[$i]}" != y ]]; then if [[ "${SRC_VALID_FS[$i]}" != y ]] || [[ "${DST_VALID_FS[$i]}" != y ]]; then
log "skipping label %s" "${LABELS[$i]}" log "skipping label %s" "${LABELS[$i]}"
continue continue
fi fi
SRCPART=/mnt/${SRCLABELS[$i]}/ SRCPART=/mnt/${SRCLABELS[$i]}/
DSTPART=/mnt/${DSTLABELS[$i]} DSTPART=/mnt/${DSTLABELS[$i]}
log -n "%s -> %s : " "$SRCPART" "$DSTPART" #log -n "%s -> %s : " "$SRCPART" "$DSTPART"
#log "\t%s %s %s %s %s" rsync "${RSYNCOPTS}" "$FILTER" "$SRCPART" "$DSTPART" #log "\t%s %s %s %s %s" rsync "${RSYNCOPTS}" "$FILTER" "$SRCPART" "$DSTPART"
skip=y copy="$COPY"
case "$DOIT" in if [[ "$COPY" == 'ask' ]]; then
yes) yesno "Copy $SRCPART to $DSTPART ? [y/n/q]" && copy=yes || copy=no
skip=n fi
;; if [[ "$copy" == yes ]]; then
no)
log "skipping (dry run)."
;;
manual)
yesno "proceed ? [y/n/q]" && skip=n
;;
esac
if [[ "$skip" == n ]]; then
# shellcheck disable=SC2086
mariadb_maybe_stop mariadb_maybe_stop
# shellcheck disable=SC2086
echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART" echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART"
fi fi
log "" #log ""
done done
# grub install # grub install
# mount virtual devices if [[ "$GRUB" == yes ]]; then
if [[ $GRUBINSTALL == yes ]]; then
log "installing grub on $DST..." log "installing grub on $DST..."
DSTMNT="/mnt/$DSTROOTLABEL" DSTMNT="/mnt/$DSTROOTLABEL"
mount -o bind /sys "$DSTMNT/sys" # mount virtual devices
mount -o bind /proc "$DSTMNT/proc" echorun mount -o bind /sys "$DSTMNT/sys"
mount -o bind /dev "$DSTMNT/dev" echorun mount -o bind /proc "$DSTMNT/proc"
echorun mount -o bind /dev "$DSTMNT/dev"
chroot "$DSTMNT" update-grub
chroot "$DSTMNT" grub-install "$DST"
echorun chroot "$DSTMNT" update-grub
echorun chroot "$DSTMNT" grub-install "$DST"
fi fi
exit 0 exit 0