Last active
February 6, 2023 16:17
-
-
Save djkazic/2f3d519639fbb78cf973fedc106f2663 to your computer and use it in GitHub Desktop.
zdefrag
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Allows you to use a [tank] and [reserve] pool | |
# Set vars for best results | |
if [ "$#" != 4 ]; then | |
echo " Usage: ./defrag <src-pool> <src-dataset> <dest-pool> <grub-disk>" | |
echo '' | |
echo " Example: ./defrag tank os reserve /dev/sda" | |
exit 1 | |
fi | |
PRIMARY="$1" | |
DATASET="$2" | |
SECONDARY="$3" | |
GRUB_DISK="$4" | |
echo "Performing switch from [$PRIMARY/$DATASET] -> [$SECONDARY/$DATASET]" | |
echo '' | |
echo "Pre-check (1/3) verifying pools existence" | |
if [ `zpool list | grep "^$PRIMARY " | wc -l` = 1 ] && [ `zpool list | grep "^$SECONDARY" | wc -l` = 1 ]; then | |
echo "[OK] Pools [$PRIMARY] and [$SECONDARY] exist!" | |
else | |
echo "Pools [$PRIMARY] and [$SECONDARY] do not exist on this machine ... aborting" | |
exit 1 | |
fi | |
echo '' | |
echo "Pre-check (2/3) verifying pool sizes" | |
convert_to_abs_size() { | |
local SIZE="$1" | |
local TIER="$2" | |
if [[ "${TIER}" = "G" ]]; then | |
SIZE=`bc <<< "1000 * $SIZE"` | |
elif [[ "${TIER}" = "T" ]]; then | |
SIZE=`bc <<< "1000 * 1000 * $SIZE"` | |
fi | |
echo "$SIZE" | |
} | |
round_to_int() { | |
local NUM="$1" | |
echo "($NUM + 0.5) / 1" | bc | |
} | |
# Grabs the allocation used by current pool | |
PRIMARYSIZEFULL=`zpool list | grep "^$PRIMARY" | sed -e's/ */ /g' | cut -d' ' -f3` | |
PRIMARYSIZE=`echo "${PRIMARYSIZEFULL%?}"` | |
PRIMARYTIER=`echo "${PRIMARYSIZEFULL: -1}"` | |
PRIMARYSIZE=`convert_to_abs_size ${PRIMARYSIZE} ${PRIMARYTIER}` | |
# Grabs the available space for the destination pool | |
SECONDARYSIZEFULL=`zpool list | grep "^$SECONDARY" | sed -e's/ */ /g' | cut -d' ' -f4` | |
SECONDARYSIZE=`echo "${SECONDARYSIZEFULL%?}"` | |
SECONDARYTIER=`echo "${SECONDARYSIZEFULL: -1}"` | |
SECONDARYSIZE=`convert_to_abs_size ${SECONDARYSIZE} ${SECONDARYTIER}` | |
# Round both numbers to ensure integer calc | |
PRIMARYSIZE=`round_to_int "${PRIMARYSIZE}"` | |
SECONDARYSIZE=`round_to_int "${SECONDARYSIZE}"` | |
# Compares the sizes | |
if [ "$PRIMARYSIZE" -lt "$SECONDARYSIZE" ]; then | |
echo "[OK] Target free size is greater than source alloc size" | |
else | |
echo "Not enough free space on target pool! Aborting ..." | |
exit 1 | |
fi | |
echo '' | |
echo "Pre-check (3/3) Checking current mountpoint" | |
if [[ `zfs mount | grep "^$SECONDARY/$DATASET */" | wc -l` = 1 ]]; then | |
echo "Target dataset [$SECONDARY/$DATASET] is already mounted to / ... aborting" | |
exit 1 | |
else | |
echo "[OK] Target dataset is unmounted" | |
fi | |
echo '' | |
echo "(1/10) Clearing old [$PRIMARY@defrag] snapshot" | |
zfs destroy -r $PRIMARY@defrag | |
echo '' | |
echo "(2/10) Creating new [$PRIMARY@defrag] snapshot!" | |
zfs snap -r $PRIMARY@defrag | |
echo '' | |
echo "(3/10) Removing target pool [$SECONDARY] datasets" | |
zfs destroy -r $SECONDARY | |
echo '' | |
echo "(4/10) Replicating defrag snapshot [$PRIMARY] -> [$SECONDARY]" | |
zfs send -Rv $PRIMARY@defrag | zfs recv -vF $SECONDARY | |
echo '' | |
echo "(5/10) Preparing mountspace for /chroot" | |
umount -R /chroot >/dev/null | |
mkdir -p /chroot | |
rm -rf /chroot/* | |
echo '' | |
echo "(6/10) Changing target mountpoint to /chroot" | |
zfs set mountpoint=none $SECONDARY | |
zfs set mountpoint=/chroot $SECONDARY/$DATASET | |
echo '' | |
echo "(7/10) Mounting [$SECONDARY/$DATASET]" | |
zfs mount $SECONDARY/$DATASET | |
cd /chroot | |
echo '' | |
echo "(8/10) Binding mounts for chroot" | |
mount --bind /dev dev | |
mount --bind /proc proc | |
mount --bind /run run | |
mount --bind /sys sys | |
# EFI support | |
# mount --bind /boot/efi boot/efi | |
echo 'Done!' | |
echo '' | |
# We need to set mountpoints from within the chroot | |
echo "(9/10) Diving into chroot" | |
cat << EOF | chroot . /bin/bash | |
zfs list | |
update-grub | |
grub-install $GRUB_DISK | |
update-initramfs -u -t -k all | |
zfs set mountpoint=/ $SECONDARY/$DATASET | |
zfs set mountpoint=none $PRIMARY | |
zfs set mountpoint=none $PRIMARY/$DATASET | |
zfs list | |
EOF | |
echo '' | |
umount -R /chroot >/dev/null | |
echo "(10/10) Reboot prompt" | |
echo '' | |
read -p "Would you like to switch to your original pool, [$PRIMARY]? (y/n) " -n 1 -r | |
echo | |
if [[ $REPLY =~ ^[Yy]$ ]]; then | |
echo "In order to do that, you'll have to destroy zpool [$PRIMARY] and re-create it on the next boot" | |
echo "Once you do that, re-run this script like this:" | |
echo " ./defrag $SECONDARY $DATASET $PRIMARY /dev/sda" | |
fi | |
echo '' | |
read -p "A reboot is required for the boot pool changes to take effect; would you like to reboot now? (y/n) " -n 1 -r | |
echo | |
if [[ $REPLY =~ ^[Yy]$ ]]; then | |
reboot | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment