Skip to content

Instantly share code, notes, and snippets.

@stevenengland
Last active January 21, 2024 19:00
Show Gist options
  • Save stevenengland/101843b9e3f2e9f2a07e079a5402eb72 to your computer and use it in GitHub Desktop.
Save stevenengland/101843b9e3f2e9f2a07e079a5402eb72 to your computer and use it in GitHub Desktop.
Shell script to boot into a live distribution of Clonezilla. Can be used in conjunctoin with LVM, is reachable via SSH, needs no monitor attached.
#!/bin/bash
# See https://steven-england.info/2023/12/24/remote-headless-image-backup-and-restore-of-proxmox-hosts-with-clonezilla/ for more explaination.
###################################################
# Variables
###################################################
# These files will be temporarily written to update grub.cfg accordingly. Normally there is no need to edit these vars.
GRUBFILE_HEADER=/etc/grub.d/01_clonezilla_header
GRUBFILE_MENU=/etc/grub.d/30_clonezilla
# Set Keyboard layout that the SSH terminal should use
I8N_KBD_LAYOUT="de"
# This ISO file must be a "live"variant of Clonezilla
# Download iso from https://deac-riga.dl.sourceforge.net/project/clonezilla/clonezilla_live_alternative/20231102-mantic/clonezilla-live-20231102-mantic-amd64.iso to $ISOFILE
ISOFILE="/boot/clonezilla.iso"
# Network parameters for the SSH server
NET_CLIENT_IP="192.168.2.51"
NET_GW_IP="192.168.2.1"
NET_MASK="255.255.255.0"
NET_ETH_PORT="enp0s3"
# Path to PVE root, neccessary to set this as kernel parameter on LVMs
PATH_TO_PVE_ROOT=/dev/mapper/pve-root
# Test for sufficient priviliges
if ! [ -w "/boot/grub/grub.cfg" ]
then
printf "[\e[31m-\e[0m] Please run this script as a privileged user (i.e. with the help of sudo)\n"
exit 1
fi
printf "[\e[36m-\e[0m] Preparing the environment for a temporary boot into Clonezilla\n"
###################################################
# Preparing GRUB HEADER
###################################################
printf "[\e[36m-\e[0m] Preparing GRUB HEADER\n"
SWDATETIME=`date -u +"%Y-%m-%d %H:%M:%S"`
HWDATETIME=`TZ=UTC hwclock | cut -d "." -f1`
echo -e "Just informative:"
echo -e ">> The actual software clock date and time is (UTC): $SWDATETIME"
echo -e ">> The actual hardware clock date and time is (UTC): $HWDATETIME"
echo -e "The hardware time is going to be used for the operation because GRUB refers to that one."
ENDDATETIME=$(date -d "$HWDATETIME 5 minutes" +"%Y-%m-%d %H:%M:%S")
# Parse the output into variables
YEAR=$(echo "$ENDDATETIME" | awk '{print $1}' | cut -d "-" -f1)
MONTH=$(echo "$ENDDATETIME" | awk '{print $1}' | cut -d "-" -f2)
DAY=$(echo "$ENDDATETIME" | awk '{print $1}' | cut -d "-" -f3)
HOUR=$(echo "$ENDDATETIME" | awk '{print $2}' | cut -d ":" -f1)
MINUTE=$(echo "$ENDDATETIME" | awk '{print $2}' | cut -d ":" -f2)
SECOND=$(echo "$ENDDATETIME" | awk '{print $2}' | cut -d ":" -f3)
cat << EOF > $GRUBFILE_HEADER
#!/bin/sh
exec tail -n +3 \$0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
insmod datehook
if [ \$MONTH -lt 10 ]; then PADMONTH="0"; else PADMONTH=""; fi
if [ \$DAY -lt 10 ]; then PADDAY="0"; else PADDAY=""; fi
if [ \$HOUR -lt 10 ]; then PADHOUR="0"; else PADHOUR=""; fi
if [ \$MINUTE -lt 10 ]; then PADMINUTE="0"; else PADMINUTE=""; fi
if [ \$SECOND -lt 10 ]; then PADSECOND="0"; else PADSECOND=""; fi
ACTDATE=\$YEAR\$PADMONTH\$MONTH\$PADDAY\$DAY
ACTTIME=\$HOUR\$PADMINUTE\$MINUTE\$PADSECOND\$SECOND
ENDDATE=$YEAR$MONTH$DAY
ENDTIME=$HOUR$MINUTE$SECOND
echo "Clonezilla time based boot decision:"
echo "- Time now: \$ACTDATE-\$PADHOUR\$ACTTIME"
echo "- Time max: \$ENDDATE-\$ENDTIME"
if [ \$ENDDATE -ge \$ACTDATE -a \$ENDTIME -ge \$ACTTIME ]; then
echo "--> Setting Clonezilla as boot entry"
set default="Clonezilla"
set next_entry=
else
echo "--> Don't set Clonezolla as boot entry"
fi
sleep --verbose 5
EOF
chmod +x $GRUBFILE_HEADER
###################################################
# Preparing GRUB MENU
###################################################
printf "[\e[36m-\e[0m] Preparing GRUB MENU\n"
KERNEL_PARAMS="\
boot=live \
live-media=$PATH_TO_PVE_ROOT \
findiso=$ISOFILE \
username=user \
usercrypted='\$y\$j9T\$YBiChb.DicuF4zqZVrXyb/\$3aXx6w.C919TAzlZ2t8NIKYau6E7TFJXJlYfNrb4IP3' \
toram=filesystem.squashfs \
ip=$NET_CLIENT_IP::$NET_GW_IP:$NET_MASK::$NET_ETH_PORT \
config components quiet noswap edd=on nomodeset keyboard-layouts=$I8N_KBD_LAYOUT \
"
CLONEZILLA_PARAMS="\
ocs_live_run=\"ocs-live-general\" ocs_live_extra_param=\"\" ocs_live_batch=\"no\" \
ocs_daemonon=\"ssh\" \
"
cat << EOF > $GRUBFILE_MENU
#!/bin/sh
exec tail -n +3 \$0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry "Clonezilla" {
insmod lvm
insmod ext2
loopback loop $ISOFILE
linux (loop)/live/vmlinuz $KERNEL_PARAMS $CLONEZILLA_PARAMS
initrd (loop)/live/initrd.img
}
EOF
chmod +x $GRUBFILE_MENU
###################################################
# Committing the changes for temporary usage
###################################################
printf "[\e[36m-\e[0m] Updating GRUB's config (/boot/grub/grub.cfg)...\n"
update-grub > /dev/null
read -p "Disable save_env command in grub.cfg (important on LVM etc.)? [Y/N]" -n 1 -r DISABLE_SAVE_ENV
echo # (optional) move to a new line
if [[ $DISABLE_SAVE_ENV =~ ^[Yy]$ ]]
then
sed -i 's/save_env/#save_env/g' /boot/grub/grub.cfg
echo -e "Disabled..."
fi
rm $GRUBFILE_HEADER
rm $GRUBFILE_MENU
printf "[\e[32m-\e[0m] All done, ready to boot into Clonezilla until $YEAR-$MONTH-$DAY $HOUR:$MINUTE:$SECOND \n"
read -p "Shall the reboot be performed now? [Y/N]" -n 1 -r REBOOT_HOST
echo # (optional) move to a new line
if [[ $REBOOT_HOST =~ ^[Yy]$ ]]
then
echo -e "Rebooting..."
reboot
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment