#!/bin/bash
# The script is designed to create or restore an image of a virtual machine kvm (c) 2018
# author Koshuba V.O.
# license: MIT
# version: 4.0.0
##
#
path_kvm=( "/kvm/win7x64"
"/kvm/win2kx64" ); # path kvm machines
path_bak=( "/arhive/backup/kvm/win7x64"
"/arhive/backup/kvm/win2kx64" ); # path backup kvm machines
img_dev=( "disk_c.img"
"disk_c.img" ); # storage kvm
img_part=( '"sda1" "sda2"'
'"sda1"' ); # sections in media kvm
img_offset=( '"1048576" "105906176"'
'"32256"' ); # offset for switch section media kvm
#
#
version="4.0.0";
owner="(c) script by Koshuba V.O. 2018";
msg_dat=( '"Script to save or restore the image KVM" "Скрипт сохранения или восстановления образа KVM"'
'"MAIN MENU" "ГЛАВНОЕ МЕНЮ"'
'"RESTORE KVM MACHINE" "ВОССТАНОВЛЕНИЕ ГОСТЕВОЙ МАШИНЫ KVM"'
'"SAVE KVM MACHINE" "СОХРАНЕНИЕ ГОСТЕВОЙ МАШИНЫ KVM"'
'"EXIT" "ВЫХОД"'
'"Today:" "Сегодня:"'
'"Available free space from Backup:" "Доступно свободного места в"'
'"SAVE GUEST MACHINE KVM" "СОХРАНИТЬ ГОСТЕВУЮ МАШИНУ KVM"'
'"RESTORE GUEST MACHINE KVM" "ВОССТАНОВИТЬ ГОСТЕВУЮ МАШИНУ KVM"'
'"Quit the program" "Выйти из программы"'
'"Select kvm machine" "Выберите машину KVM"'
'"Select kvm image" "Выберите образ KVM"'
'"{Space} key selection" "Выбор клавиша {Пробел}"'
'"Not found dialog package, please install." "Не найден пакет: dialog, пожалуйста установите."'
'"Found the file kvmsnapshot.lock - work is stopped!" "Найден файл kvmsnapshot.lok - работа остановлена!"'
'"Starting kvm-snapshot script." "Запуск скрипта kvm-snapshot."'
'"Ending work kvm-snapshot script." "Окончание работы скрипта kvm-snapshot."'
'"Not found save images!" "Не найдены сохраненные образы!"' );
# msg[17]
msg=(); # set current messages
esc_title=""; # title escape menu
esc_msg=""; # message escape menu
esc_menu=""; # return menu escape
set_lang="0"; # lang messages
menu_mode=""; # type menu select kvm
set_kvm=""; # selected kvm for processing
set_img=""; # selected image for restore kvm machine
rdate=$(date +%d.%m.%y); # real data
signlok="/tmp/run-kvmsnapshot.lok;"; # sign of work
scriptlog="/var/log/vm.log"; # log file
DIALOG=${DIALOG=dialog}; # dialog lib
#
: ${DIALOG_OK=0}
: ${DIALOG_CANCEL=1}
: ${DIALOG_HELP=2}
: ${DIALOG_EXTRA=3}
: ${DIALOG_ITEM_HELP=4}
: ${DIALOG_ESC=255}
#
scr_tmp=$(tempfile 2>/dev/null) || scr_tmp=/tmp/choise.tmp; # tmp file
trap "rm -f $scr_tmp" 0 1 2 5 15;
dform="/tmp/dform.tmp";
#
#
operation_menu=( "clear" "getTools" "langMsg" "testproc" "sMainMenu" );
operation_savekvm=( "clear" "saveKvm" "endproc" "exit" );
operation_restorekvm=( "clear" "restoreKvm" "endproc" "exit" );
operation_exit=( "clear" "endproc" "exit" );
#
#
get_tools=( "ntfsclone" "pigz" "locale" "du" "ntfsfix" "gunzip" ); # tools for script
#
#
function sMainMenu() {
local check_dialog=$(apt-cache policy dialog | grep status|wc -m );
if [[ $check_dialog = 0 ]];
then
echo -e "${msg[13]}";
sleep 2
exit 0
fi
#
esc_title="${msg[4]}";
esc_msg="${msg[9]}";
esc_menu="sMainMenu";
#
$DIALOG --clear --backtitle " ${msg[0]} | $owner | version: $version" \
--title "${msg[1]}" "$@" \
--menu " ${msg[5]} $(date +%c) \n
==============================================\n
\n" 20 51 5 \
"1." "${msg[3]}" \
"2." "${msg[2]}" \
"3." "${msg[4]}" 2>$scr_tmp;
input=$(cat $scr_tmp && rm -f $scr_tmp);
case "$input" in
"1." | "1." )
menu_mode="${msg[7]}";
menuSaveKVM;
;;
"2." | "2." )
menu_mode="${msg[8]}";
menuRestoreKVM;
;;
"3." | "3." )
escScript
;;
* )
# default select
escScript;
;;
esac
}
function escScript() {
$DIALOG --clear --backtitle " ${msg[0]} | $owner | version: $version"\
--title "$esc_title"\
--yesno "\n
\n
$esc_msg?" 10 50
case $? in
0)
clear;
execute_func=( ${operation_exit[@]} );
executor;
exit 0;
;;
1)
$esc_menu;
;;
255)
$esc_menu;
;;
esac
}
function menuSaveKVM() {
formSetKVM;
source $dform;
slc="$?";
set_kvm=$( echo $(($(cat $scr_tmp && rm -f $scr_tmp && rm -f $dform)-1)));
case $slc in
"0" | "0")
clear;
execute_func=( ${operation_savekvm[@]} );
executor;
;;
"1" | "1")
sMainMenu;
;;
"255" | "255")
sMainMenu;
;;
esac
}
function menuRestoreKVM() {
formSetKVM;
source $dform;
slc="$?";
set_kvm=$( echo $(($(cat $scr_tmp && rm -f $scr_tmp && rm -f $dform)-1)));
case $slc in
"0" | "0")
clear;
menuImg;
;;
"1" | "1")
sMainMenu;
;;
"255" | "255")
sMainMenu;
;;
esac
}
function menuImg() {
formSetImg;
source $dform;
slc="$?";
set_img=$(cat $scr_tmp && rm -f $scr_tmp && rm -f $dform);
case $slc in
"0" | "0")
clear;
execute_func=( ${operation_restorekvm[@]} );
executor;
;;
"1" | "1")
menuRestoreKVM;
;;
"255" | "255")
menuRestoreKVM;
;;
esac
}
#
#
#
testproc(){
if [ -f $scriptlog ];
then
echo > $scriptlog;
fi
if [ -f "$signlok" ];
then
echo "$(date) -- ${msg[14]}">>$scriptlog;
exit 0;
else
echo '1'>$signlok; # up trigger
echo "$(date) -- ${msg[15]}" >>$scriptlog;
fi
}
endproc(){
if [ -f $signlok ];
then
rm -f $signlok;
fi
echo "$(date) -- ${msg[16]}" >> $scriptlog;
}
#
function executor() {
if [[ ${#execute_func[@]} -eq 0 ]]
then echo "exit";
exit 0;
fi
for ((ex_index=0; ex_index != ${#execute_func[@]}; ex_index++))
do
## !! debug operation...
##echo "execution: function ${execute_func[ex_index]}"
${execute_func[ex_index]};
done
}
#
function getTools() {
for ((itools=0; itools != ${#get_tools[@]}; itools++))
do
eval get_${get_tools[$itools]}=$(whereis -b ${get_tools[$itools]}|awk '/^'${get_tools[$itools]}':/{print $2}');
list_tools[${#list_tools[@]}]="$(whereis -b ${get_tools[$itools]}|awk '/^'${get_tools[$itools]}':/{print $2}')";
done
}
#
function langMsg() {
if [[ ! $($get_locale|grep 'LANG='|sed 's/\LANG=//g'|grep 'ru_RU.UTF-8'|wc -m) -eq 0 ]];
then
set_lang="1";
else
set_lang="0";
fi
for ((ilang=0; ilang != ${#msg_dat[@]}; ilang++))
do
eval tmsg="(" $(echo -e ${msg_dat[$ilang]}) ")";
msg[$ilang]=${tmsg[$set_lang]};
done
}
function formSetKVM() {
echo>$dform;
echo '$DIALOG --backtitle " '${msg[0]}' | '$owner'" \'>>$dform
echo ' --title "'$menu_mode'" --clear \' >>$dform
echo ' --radiolist "\n '${msg[10]}'"' '20 60 '${#path_kvm[@]}' \' >>$dform
#
if [[ ${#path_kvm[@]} = 0 ]];
then
echo " don't find KVM!"
sleep 1;
rm -f $dform;
exit 0;
fi
for ((ilist=0; ilist != ${#path_kvm[@]}; ilist++))
do
local fmark='';
local dmark='';
if [[ $ilist = 0 ]];
then
fmark='ON';
else
fmark='off';
fi
if [[ ! $ilist = $(echo $((${#path_kvm[@]} -1))) ]];
then
dmark='\';
else
dmark='2> $scr_tmp';
fi
echo -e '"'"$(echo $(($ilist+1)))"'"' '"'"${path_kvm[$ilist]}"'"' $fmark $dmark >>$dform;
done
}
function formSetImg() {
local get_path_bak=${path_bak[$set_kvm]};
eval local get_imgs="(" $(find "$get_path_bak"/* -maxdepth 0 -type d -printf '%f\n') ")";
echo>$dform;
echo '$DIALOG --backtitle " '${msg[0]}' | '$owner'" \'>>$dform
echo ' --title "'$menu_mode'" --clear \' >>$dform
echo ' --radiolist " '${msg[11]}': '$get_path_bak'\n '${msg[12]}'"' '20 60 '${#get_imgs[@]}' \' >>$dform
#
if [[ ${#get_imgs[@]} = 0 ]];
then
rm -f $dform;
#
esc_title="${msg[4]}";
esc_msg="${msg[17]} \n ${msg[9]}";
esc_menu="menuRestoreKVM";
#
escScript;
fi
for ((iget=0; iget != ${#get_imgs[@]}; iget++))
do
local fmark='';
local dmark='';
if [[ $iget = 0 ]];
then
fmark='ON';
else
fmark='off';
fi
if [[ ! $iget = $(echo $((${#get_imgs[@]} -1))) ]];
then
dmark='\';
else
dmark='2> $scr_tmp';
fi
echo -e '"'"${get_imgs[$iget]}"'"' '""' $fmark $dmark >>$dform;
done
}
function saveKvm() {
local save_kvm="${path_kvm[$set_kvm]}";
local save_bak="${path_bak[$set_kvm]}/$rdate";
local save_img="${img_dev[$set_kvm]}";
eval local save_dev="(" $(echo -e ${img_part[$set_kvm]}) ")";
eval local save_offset="(" "$(echo -e ${img_offset[$set_kvm]})" ")";
local set_loop_main="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
losetup /dev/$set_loop_main $save_kvm/$save_img;
if [[ ! -d $save_bak ]];
then
mkdir -p $save_bak;
fi
sfdisk -d /dev/$set_loop_main >$save_bak/info.txt;
dd if=/dev/$set_loop_main of=$save_bak/winboot.img bs=512 count=1;
for ((iskvm=0; iskvm != ${#save_dev[@]}; iskvm++))
do
local set_loop_dev="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
losetup -o ${save_offset[$iskvm]} /dev/$set_loop_dev /dev/$set_loop_main;
ntfsclone --save-image -o - /dev/$set_loop_dev| pigz -p2 -c9>$save_bak/${save_dev[$iskvm]}.img.gz;
losetup -d /dev/$set_loop_dev;
done
losetup -d /dev/$set_loop_main;
}
function restoreKvm() {
local rest_kvm="${path_kvm[$set_kvm]}";
local rest_bak="${path_bak[$set_kvm]}/$set_img";
local rest_img="${img_dev[$set_kvm]}";
eval local rest_dev="(" $(echo -e ${img_part[$set_kvm]}) ")";
eval local rest_offset="(" "$(echo -e ${img_offset[$set_kvm]})" ")";
local set_loop_rmain="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
losetup /dev/$set_loop_rmain $rest_kvm/$rest_img;
dd if=$rest_bak/winboot.img of=/dev/$set_loop_rmain bs=512 count=1;
sleep 1
for ((irkvm=0; irkvm != ${#rest_dev[@]}; irkvm++))
do
local set_loop_rdev="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
losetup -o ${rest_offset[$irkvm]} /dev/$set_loop_rdev /dev/$set_loop_rmain;
gunzip -c $rest_bak/${rest_dev[$irkvm]}.img.gz|ntfsclone --restore-image -O /dev/$set_loop_rdev -;
losetup -d /dev/$set_loop_rdev;
done
losetup -d /dev/$set_loop_rmain;
}
#
execute_func=( ${operation_menu[@]} );
executor;