kvm-snapshot.sh 13 KB

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