kvm-snapshot.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. #!/bin/bash
  2. # The script is designed to create or restore an image of a virtual machine kvm (c) 2018
  3. # author Koshuba V.O.
  4. # license: MIT
  5. # version: 4.0.10
  6. ##
  7. #<manual_parameters>
  8. path_kvm=( "/kvm/ms1" "/kvm/ms2" ); # path kvm machines
  9. path_bak=( "/backup/kvm/ms1" "/backup/kvm/ms2" ); # path backup kvm machines
  10. img_dev=( "sda.img" "sda.img" ); # storage kvm
  11. img_part=( '"sda1" "sda2" "sda3" "sda4"'
  12. '"sda1" "sda2" "sda3" "sda4"' ); # sections in media kvm
  13. img_type=( '"dd" "dd" "dd" "ntfsclone"'
  14. '"dd" "dd" "dd" "ntfsclone"' ); # type backup partition:dd[fat,EFI System,Windows recovery environment,Microsoft reserved],ntfsclone[ntfs,Microsoft basic data]
  15. img_size=( '"300" "99" "128" ""'
  16. '"300" "99" "128" ""' ); # size partition for dd (Mb)
  17. img_offset=( '"1048576" "315621376" "419430400" "553648128"'
  18. '"1048576" "315621376" "419430400" "553648128"' ); # offset for switch section media kvm
  19. #</manual_parameters>
  20. #<script_value>
  21. version="4.0.10";
  22. owner="(c) script by Koshuba V.O. 2018";
  23. msg_dat=( '"Script to save or restore the image KVM" "Скрипт сохранения или восстановления образа KVM"'
  24. '"MAIN MENU" "ГЛАВНОЕ МЕНЮ"'
  25. '"RESTORE KVM MACHINE" "ВОССТАНОВЛЕНИЕ ГОСТЕВОЙ МАШИНЫ KVM"'
  26. '"SAVE KVM MACHINE" "СОХРАНЕНИЕ ГОСТЕВОЙ МАШИНЫ KVM"'
  27. '"EXIT" "ВЫХОД"'
  28. '"Today:" "Сегодня:"'
  29. '"Available free space from Backup:" "Доступно свободного места в"'
  30. '"SAVE GUEST MACHINE KVM" "СОХРАНИТЬ ГОСТЕВУЮ МАШИНУ KVM"'
  31. '"RESTORE GUEST MACHINE KVM" "ВОССТАНОВИТЬ ГОСТЕВУЮ МАШИНУ KVM"'
  32. '"Quit the program" "Выйти из программы"'
  33. '"Select kvm machine" "Выберите машину KVM"'
  34. '"Select kvm image" "Выберите образ KVM"'
  35. '"{Space} key selection" "Выбор клавиша {Пробел}"'
  36. '"Not found dialog package, please install." "Не найден пакет: dialog, пожалуйста установите."'
  37. '"Found the file kvmsnapshot.lock - work is stopped!" "Найден файл kvmsnapshot.lok - работа остановлена!"'
  38. '"Starting kvm-snapshot script." "Запуск скрипта kvm-snapshot."'
  39. '"Ending work kvm-snapshot script." "Окончание работы скрипта kvm-snapshot."'
  40. '"Not found save images!" "Не найдены сохраненные образы!"'
  41. '"The installed type was not found from the parameters for image processing - dd,ntfsclone,mrsrv!" "Не найден установленный тип из параметров для обработки образа - dd,ntfsclone,mrsrv!"' );
  42. # msg[17]
  43. msg=(); # set current messages
  44. esc_title=""; # title escape menu
  45. esc_msg=""; # message escape menu
  46. esc_menu=""; # return menu escape
  47. set_lang="0"; # lang messages
  48. menu_mode=""; # type menu select kvm
  49. set_kvm=""; # selected kvm for processing
  50. set_img=""; # selected image for restore kvm machine
  51. rdate=$(date +%d.%m.%y); # real data
  52. signlok="/tmp/run-kvmsnapshot.lok;"; # sign of work
  53. scriptlog="/var/log/vm.log"; # log file
  54. DIALOG=${DIALOG=dialog}; # dialog lib
  55. #<Define the dialog exit status codes>
  56. : ${DIALOG_OK=0}
  57. : ${DIALOG_CANCEL=1}
  58. : ${DIALOG_HELP=2}
  59. : ${DIALOG_EXTRA=3}
  60. : ${DIALOG_ITEM_HELP=4}
  61. : ${DIALOG_ESC=255}
  62. #</Define>
  63. scr_tmp=$(tempfile 2>/dev/null) || scr_tmp=/tmp/choise.tmp; # tmp file
  64. trap "rm -f $scr_tmp" 0 1 2 5 15;
  65. dform="/tmp/dform.tmp";
  66. #</script_value>
  67. #<functions_&_operations>
  68. operation_menu=( "clear" "getTools" "langMsg" "testproc" "sMainMenu" );
  69. operation_savekvm=( "clear" "saveKvm" "endproc" "exit" );
  70. operation_restorekvm=( "clear" "restoreKvm" "endproc" "exit" );
  71. operation_exit=( "clear" "endproc" "exit" );
  72. #</function_&_operations>
  73. #<tools_script>
  74. get_tools=( "ntfsclone" "pigz" "locale" "du" "ntfsfix" "gunzip" ); # tools for script
  75. #</tools_script>
  76. #<dialogs>
  77. function sMainMenu() {
  78. local check_dialog=$(apt-cache policy dialog | grep status|wc -m );
  79. if [[ $check_dialog = 0 ]];
  80. then
  81. echo -e "${msg[13]}";
  82. sleep 2
  83. exit 0
  84. fi
  85. #<esc_value>
  86. esc_title="${msg[4]}";
  87. esc_msg="${msg[9]}";
  88. esc_menu="sMainMenu";
  89. #</esc_value>
  90. $DIALOG --clear --backtitle " ${msg[0]} | $owner | version: $version" \
  91. --title "${msg[1]}" "$@" \
  92. --menu " ${msg[5]} $(date +%c) \n
  93. ==============================================\n
  94. \n" 20 51 5 \
  95. "1." "${msg[3]}" \
  96. "2." "${msg[2]}" \
  97. "3." "${msg[4]}" 2>$scr_tmp;
  98. input=$(cat $scr_tmp && rm -f $scr_tmp);
  99. case "$input" in
  100. "1." | "1." )
  101. menu_mode="${msg[7]}";
  102. menuSaveKVM;
  103. ;;
  104. "2." | "2." )
  105. menu_mode="${msg[8]}";
  106. menuRestoreKVM;
  107. ;;
  108. "3." | "3." )
  109. escScript
  110. ;;
  111. * )
  112. # default select
  113. escScript;
  114. ;;
  115. esac
  116. }
  117. function escScript() {
  118. $DIALOG --clear --backtitle " ${msg[0]} | $owner | version: $version"\
  119. --title "$esc_title"\
  120. --yesno "\n
  121. \n
  122. $esc_msg?" 10 50
  123. case $? in
  124. 0)
  125. clear;
  126. execute_func=( ${operation_exit[@]} );
  127. executor;
  128. exit 0;
  129. ;;
  130. 1)
  131. $esc_menu;
  132. ;;
  133. 255)
  134. $esc_menu;
  135. ;;
  136. esac
  137. }
  138. function menuSaveKVM() {
  139. formSetKVM;
  140. source $dform;
  141. slc="$?";
  142. set_kvm=$( echo $(($(cat $scr_tmp && rm -f $scr_tmp && rm -f $dform)-1)));
  143. case $slc in
  144. "0" | "0")
  145. clear;
  146. execute_func=( ${operation_savekvm[@]} );
  147. executor;
  148. ;;
  149. "1" | "1")
  150. sMainMenu;
  151. ;;
  152. "255" | "255")
  153. sMainMenu;
  154. ;;
  155. esac
  156. }
  157. function menuRestoreKVM() {
  158. formSetKVM;
  159. source $dform;
  160. slc="$?";
  161. set_kvm=$( echo $(($(cat $scr_tmp && rm -f $scr_tmp && rm -f $dform)-1)));
  162. case $slc in
  163. "0" | "0")
  164. clear;
  165. menuImg;
  166. ;;
  167. "1" | "1")
  168. sMainMenu;
  169. ;;
  170. "255" | "255")
  171. sMainMenu;
  172. ;;
  173. esac
  174. }
  175. function menuImg() {
  176. formSetImg;
  177. source $dform;
  178. slc="$?";
  179. set_img=$(cat $scr_tmp && rm -f $scr_tmp && rm -f $dform);
  180. case $slc in
  181. "0" | "0")
  182. clear;
  183. execute_func=( ${operation_restorekvm[@]} );
  184. executor;
  185. ;;
  186. "1" | "1")
  187. menuRestoreKVM;
  188. ;;
  189. "255" | "255")
  190. menuRestoreKVM;
  191. ;;
  192. esac
  193. }
  194. #</dialogs>
  195. #<functions>
  196. #<Fn test lock file>
  197. testproc(){
  198. if [ -f $scriptlog ];
  199. then
  200. echo > $scriptlog;
  201. fi
  202. if [ -f "$signlok" ];
  203. then
  204. echo "$(date) -- ${msg[14]}">>$scriptlog;
  205. exit 0;
  206. else
  207. echo '1'>$signlok; # up trigger
  208. echo "$(date) -- ${msg[15]}" >>$scriptlog;
  209. fi
  210. }
  211. endproc(){
  212. if [ -f $signlok ];
  213. then
  214. rm -f $signlok;
  215. fi
  216. echo "$(date) -- ${msg[16]}" >> $scriptlog;
  217. }
  218. #<Fn_executor>
  219. function executor() {
  220. if [[ ${#execute_func[@]} -eq 0 ]]
  221. then echo "exit";
  222. exit 0;
  223. fi
  224. for ((ex_index=0; ex_index != ${#execute_func[@]}; ex_index++))
  225. do
  226. ## !! debug operation...
  227. ##echo "execution: function ${execute_func[ex_index]}"
  228. ${execute_func[ex_index]};
  229. done
  230. }
  231. #<Fn_get-tools>
  232. function getTools() {
  233. for ((itools=0; itools != ${#get_tools[@]}; itools++))
  234. do
  235. eval get_${get_tools[$itools]}=$(whereis -b ${get_tools[$itools]}|awk '/^'${get_tools[$itools]}':/{print $2}');
  236. list_tools[${#list_tools[@]}]="$(whereis -b ${get_tools[$itools]}|awk '/^'${get_tools[$itools]}':/{print $2}')";
  237. done
  238. }
  239. #<Fn_get-lang>
  240. function langMsg() {
  241. if [[ ! $($get_locale|grep 'LANG='|sed 's/\LANG=//g'|grep 'ru_RU.UTF-8'|wc -m) -eq 0 ]];
  242. then
  243. set_lang="1";
  244. else
  245. set_lang="0";
  246. fi
  247. for ((ilang=0; ilang != ${#msg_dat[@]}; ilang++))
  248. do
  249. eval tmsg="(" $(echo -e ${msg_dat[$ilang]}) ")";
  250. msg[$ilang]=${tmsg[$set_lang]};
  251. done
  252. }
  253. function formSetKVM() {
  254. echo>$dform;
  255. echo '$DIALOG --backtitle " '${msg[0]}' | '$owner'" \'>>$dform
  256. echo ' --title "'$menu_mode'" --clear \' >>$dform
  257. echo ' --radiolist "\n '${msg[10]}'"' '20 60 '${#path_kvm[@]}' \' >>$dform
  258. #<input_list>
  259. if [[ ${#path_kvm[@]} = 0 ]];
  260. then
  261. echo " don't find KVM!"
  262. sleep 1;
  263. rm -f $dform;
  264. exit 0;
  265. fi
  266. for ((ilist=0; ilist != ${#path_kvm[@]}; ilist++))
  267. do
  268. local fmark='';
  269. local dmark='';
  270. if [[ $ilist = 0 ]];
  271. then
  272. fmark='ON';
  273. else
  274. fmark='off';
  275. fi
  276. if [[ ! $ilist = $(echo $((${#path_kvm[@]} -1))) ]];
  277. then
  278. dmark='\';
  279. else
  280. dmark='2> $scr_tmp';
  281. fi
  282. echo -e '"'"$(echo $(($ilist+1)))"'"' '"'"${path_kvm[$ilist]}"'"' $fmark $dmark >>$dform;
  283. done
  284. }
  285. function formSetImg() {
  286. local get_path_bak=${path_bak[$set_kvm]};
  287. eval local get_imgs="(" $(find "$get_path_bak"/* -maxdepth 0 -type d -printf '%f\n') ")";
  288. echo>$dform;
  289. echo '$DIALOG --backtitle " '${msg[0]}' | '$owner'" \'>>$dform
  290. echo ' --title "'$menu_mode'" --clear \' >>$dform
  291. echo ' --radiolist " '${msg[11]}': '$get_path_bak'\n '${msg[12]}'"' '20 60 '${#get_imgs[@]}' \' >>$dform
  292. #<input_list>
  293. if [[ ${#get_imgs[@]} = 0 ]];
  294. then
  295. rm -f $dform;
  296. #<esc_value>
  297. esc_title="${msg[4]}";
  298. esc_msg="${msg[17]} \n ${msg[9]}";
  299. esc_menu="menuRestoreKVM";
  300. #</esc_value>
  301. escScript;
  302. fi
  303. for ((iget=0; iget != ${#get_imgs[@]}; iget++))
  304. do
  305. local fmark='';
  306. local dmark='';
  307. if [[ $iget = 0 ]];
  308. then
  309. fmark='ON';
  310. else
  311. fmark='off';
  312. fi
  313. if [[ ! $iget = $(echo $((${#get_imgs[@]} -1))) ]];
  314. then
  315. dmark='\';
  316. else
  317. dmark='2> $scr_tmp';
  318. fi
  319. echo -e '"'"${get_imgs[$iget]}"'"' '""' $fmark $dmark >>$dform;
  320. done
  321. }
  322. function saveKvm() {
  323. local save_kvm="${path_kvm[$set_kvm]}";
  324. local save_bak="${path_bak[$set_kvm]}/$rdate";
  325. local save_img="${img_dev[$set_kvm]}";
  326. eval local save_type="(" $(echo -e ${img_type[$set_kvm]}) ")";
  327. eval local save_size="(" $(echo -e ${img_size[$set_kvm]}) ")";
  328. eval local save_dev="(" $(echo -e ${img_part[$set_kvm]}) ")";
  329. eval local save_offset="(" "$(echo -e ${img_offset[$set_kvm]})" ")";
  330. local set_loop_main="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
  331. losetup /dev/$set_loop_main $save_kvm/$save_img;
  332. if [[ ! -d $save_bak ]];
  333. then
  334. mkdir -p $save_bak;
  335. fi
  336. sfdisk -d /dev/$set_loop_main >$save_bak/info_sfdisk.txt;
  337. sfdisk -l /dev/$set_loop_main >$save_bak/info_disk.txt;
  338. dd if=/dev/$set_loop_main of=$save_bak/winboot.img bs=512 count=1;
  339. for ((iskvm=0; iskvm != ${#save_dev[@]}; iskvm++))
  340. do
  341. local set_loop_dev="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
  342. losetup -o ${save_offset[$iskvm]} /dev/$set_loop_dev /dev/$set_loop_main;
  343. if [ "${save_type[$iskvm]}" == "ntfsclone" ];
  344. then
  345. ntfsclone --save-image -o - /dev/$set_loop_dev| pigz -p2 -c9>$save_bak/${save_dev[$iskvm]}_ntfs.img.gz;
  346. fi
  347. if [ "${save_type[$iskvm]}" == "dd" ];
  348. then
  349. if [ "${save_size[$iskvm]}" != "" ];
  350. then
  351. dd if=/dev/$set_loop_dev status=progress bs=${save_size[$iskvm]}M count=1|pigz -p4 -M -c9>$save_bak/${save_dev[$iskvm]}_dd.img.gz;
  352. else
  353. dd if=/dev/$set_loop_dev status=progress|pigz -p4 -M -c9>$save_bak/${save_dev[$iskvm]}_dd.img.gz;
  354. fi
  355. fi
  356. if [ "${save_type[$iskvm]}" != "ntfsclone" ] && [ "${save_type[$iskvm]}" != "dd" ] && [ "${save_type[$iskvm]}" != "mrsrv" ];
  357. then
  358. echo "[$save_bak/${save_dev[$irkvm]}_dd.img.gz]\n ${msg[18]} - >> /var/log/syslog";
  359. echo "$(date) - kvm-snapshot: [$save_bak/${save_dev[$irkvm]}_dd.img.gz] \n ${msg[18]}" >> /var/log/syslog;
  360. fi
  361. losetup -d /dev/$set_loop_dev;
  362. done
  363. losetup -d /dev/$set_loop_main;
  364. }
  365. function restoreKvm() {
  366. local rest_kvm="${path_kvm[$set_kvm]}";
  367. local rest_bak="${path_bak[$set_kvm]}/$set_img";
  368. local rest_img="${img_dev[$set_kvm]}";
  369. eval local rest_type="(" $(echo -e ${img_type[$set_kvm]}) ")";
  370. eval local rest_size="(" $(echo -e ${img_size[$set_kvm]}) ")";
  371. eval local rest_dev="(" $(echo -e ${img_part[$set_kvm]}) ")";
  372. eval local rest_offset="(" "$(echo -e ${img_offset[$set_kvm]})" ")";
  373. local set_loop_rmain="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
  374. losetup /dev/$set_loop_rmain $rest_kvm/$rest_img;
  375. dd if=$rest_bak/winboot.img of=/dev/$set_loop_rmain bs=512 count=1;
  376. sleep 1
  377. for ((irkvm=0; irkvm != ${#rest_dev[@]}; irkvm++))
  378. do
  379. local set_loop_rdev="$(losetup -f|grep -v loop-control|sed 's/\/\dev\///g')";
  380. losetup -o ${rest_offset[$irkvm]} /dev/$set_loop_rdev /dev/$set_loop_rmain;
  381. if [ ${rest_type[$irkvm]} == "ntfsclone" ];
  382. then
  383. gunzip -c $rest_bak/${rest_dev[$irkvm]}_ntfs.img.gz|ntfsclone --restore-image -O /dev/$set_loop_rdev -;
  384. fi
  385. if [ "${rest_type[$irkvm]}" == "dd" ];
  386. then
  387. if [ "${rest_size[$irkvm]}" != "" ];
  388. then
  389. pigz -p4 -c -d -M $rest_bak/${rest_dev[$irkvm]}_dd.img.gz|dd of=/dev/$set_loop_rdev status=progress bs=${rest_size[$irkvm]}M count=1;
  390. else
  391. pigz -p4 -c -d -M $rest_bak/${rest_dev[$irkvm]}_dd.img.gz|dd of=/dev/$set_loop_rdev status=progress;
  392. fi
  393. fi
  394. if [ "${rest_type[$irkvm]}" != "ntfsclone" ] && [ "${rest_type[$irkvm]}" != "dd" ];
  395. then
  396. echo "[$rest_bak/${rest_dev[$irkvm]}_dd.img.gz]\n ${msg[18]} - >> /var/log/syslog";
  397. echo "$(date) - kvm-snapshot: [$rest_bak/${rest_dev[$irkvm]}_dd.img.gz] \n ${msg[18]}" >> /var/log/syslog;
  398. fi
  399. losetup -d /dev/$set_loop_rdev;
  400. done
  401. losetup -d /dev/$set_loop_rmain;
  402. }
  403. #</functions>
  404. execute_func=( ${operation_menu[@]} );
  405. executor;