вторник, 27 октября 2009 г.

Делаем циклический backup/restore конфигов и красивое восстановление посредством dialog.



Давно стояла задача создания небольшого скрипта, по созданию резервных копий и восстановлению различных конфигурационных файлов.

Обрисую задачи, которые решает этот скрипт:
  • Скрипт создаёт резервную копию конфига, дописывая к нему дату и время, и перемещает его в папку для хранения;
  • Скрипт имеет ротацию, т.е. следит за кол-вом конфигов в папке и удаляет самый старый, если их число превышает заданное.
  • Восстановление происходит через меню в 3 этапа:
  1. выбираем конфиг, который хотим восстановить.
  2. выбираем резервную копию для восстановления.
  3. подтверждаем наш выбор.
  • Перед восстановлением скрипт делает резервную копию восстанавливаемого файла с пометкой "_before_restore".
  • В скрипте присутвует быстрое восстановление всех конфигов на их последние резервные копии.
Код и описания - под катом.


Сам код:
#!/bin/bash
#
#  Config backup/restore
#
# AUTHOR:  nol1ght (nol1ght.mail@gmail.com)
# VERSION: 1.0
# LICENSE: public
#

#Устанавливаем разделитель
Separator=";"
#Устанавливаем путь до конфига
backup_config="bconf_config" 
#Устанавливаем максимальное кол-во резервных копий конфигов
configRotate=12;
#Выбираем вид отображения диалогов. (dialog/xdialog/gdialog и все подобные)
DIALOG=${DIALOG=dialog}
#Переменная для копирования конфига перед восстановлением
backupRestore=""

number=-1;

#Функция чтения конфига
function read_config {
 #читаем построчно
 while read line
 do
 #увеличиваем счётчик
 ((number++))
 #Записываем путь до конфига(в строке до разделителя)
 pathToConfig[${number}]=${line%%$Separator*};
 #Записываем путь до каталога резервных копий(в строке после разделителя)
 pathToBackup[${number}]=${line#*$Separator};
 done < $backup_config
}

#Установка переменных пути
function paths {
#Смотрим, есть ли параметры у функции
   if [ -z "$1" ]
   then
 #если нет - выходим
   exit
   fi
 #Записываем в переменную имя конфига + избавляемся от проблем с пробелами в имени файла
 configName=`basename ${pathToConfig[$1]} | sed 's/ /\?/g'`
 #Записываем в переменную путь до конфига + избавляемся от проблем с пробелами в имени файла
 pathToConf=`dirname ${pathToConfig[$1]} | sed 's/ /\?/g'`
 #Добавляем / в конец
 pathToConf=$pathToConf"/"
 #Записываем в переменную путь до резервных копий + избавляемся от проблем с пробелами в имени файла
 pathToBack=`echo ${pathToBackup[$1]} | sed 's/ /\?/g'`
}

#Функция резервного копирования
function backup {
#Смотрим есть ли параметры у функции
   if [ -z "$1" ]
   then
 #если нет - устанавливаем на перебор всех конфигов
 i_begin=0;
 max=$number;
   else
 #если да - устанавливаем на конкретный конфиг
 i_begin=$1;
 max=$1;
   fi

#Перебираем конфиг(и)
for ((i = $i_begin; i <= $max; i++)); do
 #Задаём переменные пути
 paths $i
 
 #Задаём имя резервной копии (формат - "имя конфига_ГГГГММДД_ЧЧММСС")
 backupName=$configName"_"$(date +%Y%m%d)"_"$(date +%H%M%S)
 #Создаём каталог для резернвых копий 
 mkdir -p $pathToBack
 #Подсчитываем кол-во резервных копий в каталоге
 count=`cd $pathToBack;ls $pathToBack$configName_* | wc -l`
 #Создаём резервную копию
 cp $pathToConf$configName $pathToBack$backupName$backupRestore
 #Смотрим если число конфигов достигло максимального числа - удаляем последний.
 if [ $count -gt $configRotate ]; then
  ls -t -1 $pathToBack$configName_* | tail --lines 1 | xargs rm
 fi
done 
}

#Функция восстановления 
function restore {
#Создаём временный файл
temp_file=`tempfile 2> /dev/null`
#Перебираем все конфиги
for ((i = 0; i <= number; i++)); do
 #Подготавливаем строчку для меню
 dialog_config="$dialog_config $i \"${pathToConfig[$i]}\" "
done

#Отоброжаем меню
$DIALOG --menu "Select config to restore" 30 60 30 $dialog_config 2> $temp_file
#Читаем ответ
conf=`cat $temp_file` 
#Если ничего не выбранно - выходим из программы.
if [ -z $conf ];then 
clear
exit
fi
 #Задаём переменные пути
 paths $conf

#Смотрим кол-во резервных копий в каталоге
files=(`ls -t -1 $pathToBack$pathToConf_*`)
max_index=$((${#files[@]} - 1))

#Перебираем все конфиги
for ((i = 0; i <= max_index; i++)); do   
 filename=`basename "${files[i]}"`
 #Подготавливаем строчку для меню
 dialog_array="$dialog_array $i $filename " 
done 

#Отоброжаем меню
$DIALOG --menu "Select backup to restore" 30 60 30 $dialog_array 2> $temp_file
#Читаем ответ
answer=`cat $temp_file` 
#Если ничего не выбранно - выходим из программы.
if [ -z $answer ];then 
clear
exit
fi

#Отоброжаем меню yes/no
$DIALOG --yesno "Do you really want to restore ${files[$answer]}?" 10 70 
case $? in
  0)

 #Устанавливаем метку
 backupRestore="_before_restore"
 #Делаем резервную копию текущей конфигурации
 backup $conf
 #Восстанавливаем файл
 cp ${files[$answer]} $pathToConf$configName;;
  1)
 exit;;
  255)
 exit;;
esac

}

function restore_last {
$DIALOG --yesno "Do you really want to restore all backup files?" 10 70  
case $? in
  0)
 #Устанавливаем метку
 backupRestore="_before_mass_restore"
 #Делаем резервную копию
 backup
 #Перебираем все конфиги
 for ((i = 0; i <= number; i++)); do
  paths $i

  backupname=`ls -t -1 $pathToBack$configName_* | head --lines 1`
  cp $backupname $pathToConf$configName
 done;;
  1)
 exit;;
  255)
 exit;;
esac

}

#Функция вывода конфигов на экран
function list_config {
 echo "Configs to backup: "
 for ((i = 0; i <= number; i++)); do
  echo ${pathToConfig[$i]};
 done
}
#Функция показывает как работать с программой
function usage {
 echo "Backup/Restore Config by nol1ght v1.0"
 echo "Usage:"
 echo "-b backup config"
 echo "-l list of configs"
 echo "-r restore config"
 echo "-rl restore all last configs"
 echo "-help this message"
}

#Проверяем под рутом ли мы зашли
if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root" 1>&2
   exit 1
fi
#Загружаем конфиги
read_config
#переключаемся по пунку меню
case $1 in 
 -b)
  backup;;
 -r)
  restore;;
 -rl)
  restore_last;;
 -l)
  list_config;;
 --help)
  usage;;
esac


  
clear
exit
 

Файл bconf_config имеет следующую структуру:
Путь_до_конфига Разделитель Путь_до_каталога_хранения
К примеру:
/etc/apache2/apache2.conf;/home/nol1ght/backup/apache2/
/etc/php5/apache2/php.ini;/home/nol1ght/backup/php/

Всё теперь осталось поставить в крон, я ставлю на раз в час
0 * * * * /home/nol1ght/bconf -b

Скриншоты процесса восстановления в dialog и gdialog:


Берегите Ваши конфиги и меньше их восстанавливайте :)

Комментариев нет:

Отправить комментарий