Для восстановления резервных копий, полученных утилитой, описанной в предыдущей части, используется столь же простая утилита:
#!/sbin/sh
#
# ZRescue - ZFS Backup/Restore
#
# ZFS archives uses for bare-metal restore
# and systems cloning.
#
# Archive names can be incremental, like that:
# [hostname].[pool|pool%dataset].n.zfs<.gz>, n=0,1,2...
# Note: Do not rename archive files! Filenames will
# use to recovery purposes.
#
# Version 2.0 (C) 2009,2016 Y.Voinov
#
# If you not specify pool/dataset name in command line,
# it will be read from archive name.
#
#pragma ident "@(#)zfsrestore.sh 2.0 10/22/09 YV"
#
#############
# Variables #
#############
# Environment variables
PATH=/usr/local/bin:/sbin:/usr/bin:$PATH
# Copyright and version
PKG_NAME="ZRescue"
PKG_VERSION="2.0"
COPY="Copyright (C) 2009, Yuri Voinov"
# Archiver name
ARC_NAME="gzip"
# Archive file suffix
ARC_SUFFIX=".gz"
# GZip default compression level
COMP_LEVEL="9"
# Default archive extension
ext="zfs"
# OS utilities
BASENAME=`which basename`
CAT=`which cat`
CUT=`which cut`
DATE=`which date`
ECHO=`which echo`
EXPR=`which expr`
GETOPT=`which getopt`
FILE=`which file`
GREP=`which grep`
GZIP=`which gzip`
HOSTNAME=`which hostname`
ID=`which id`
PRINTF=`which printf`
SED=`which sed`
SSH=`which ssh`
UNAME=`which uname`
WHOAMI=`which whoami`
ZFS=`which zfs`
OS_VER=`$UNAME -r|$CUT -f2 -d"."`
OS_NAME=`$UNAME -s|$CUT -f1 -d" "`
OS_FULL=`$UNAME -sr`
# System name
system=`$HOSTNAME`
# Snapshots extension by default
# Correct it if these snapshots already exists in the system
SNAP_EXT="$system""_""$PKG_NAME""-""$PKG_VERSION""_snapshot"
###############
# Subroutines #
###############
check_os ()
{
# Check OS
$PRINTF "Checking OS... "
if [ "$OS_NAME" = "SunOS" -a "$OS_VER" -lt "10" ]; then
$ECHO "ERROR: Unsupported OS: $OS_FULL"
$ECHO "Exiting..."
exit 1
else
$ECHO "$OS_FULL"
fi
}
check_root ()
{
# Check if user root
$PRINTF "Checking super-user... "
if [ -f /usr/xpg4/bin/id ]; then
WHO=`/usr/xpg4/bin/id -n -u`
elif [ "`$ID | $CUT -f1 -d" "`" = "uid=0(root)" ]; then
WHO="root"
else
WHO=$WHOAMI
fi
if [ ! "$WHO" = "root" ]; then
$ECHO
$ECHO "ERROR: You must be super-user to run this script."
exit 1
fi
$ECHO "$WHO"
}
check_fs_exists ()
{
# Check filesystem exists
arg_fs=$1
ret=`$ZFS list -H -o name $arg_fs > /dev/null 2>&1; $ECHO $?`
if [ "$ret" != "0" ]; then
$ECHO "ERROR: ZFS pool/dataset $arg_fs does not exist."
$ECHO " Please specify another ZFS."
$ECHO "Exiting..."
exit 1
fi
}
check_hostname ()
{
# Check if backup hostname is the same target hostname
arg_backup_hostname=$1
arg_remote_hostname=$2
if [ "$arg_backup_hostname" != "$arg_remote_hostname" ]; then
$ECHO "WARNING: Backup was taken from $arg_backup_hostname host,"
$ECHO " target host is $arg_remote_hostname."
fi
}
copyright_and_version ()
{
# Print package and copyright info
$ECHO
$ECHO "$PKG_NAME $PKG_VERSION $COPY"
$ECHO
}
usage_note ()
{
copyright_and_version
$ECHO "Usage: `$BASENAME $0` [-v] [-n] [-r] [-f] [/mntpoint/archive|local fs] [pool|dataset] [host]"
$ECHO
$ECHO "Note: Compression will use if GZip installed,"
$ECHO " both for local archives or remote streams transfer."
$ECHO "Beware: GZip must be installed in both nodes in case of remote backup."
exit 1
}
archive_type ()
{
# Check archive type using extension and header check
arg_file=$1
if [ ! -z "`$ECHO $arg_file | $GREP $ARC_SUFFIX$`" -a \
! -z "`$FILE "$arg_file" | $GREP $ARC_NAME`" ]; then
$ECHO "$ARC_NAME"
elif [ ! -z "`$ECHO $arg_file | $GREP $ext$`" -a \
! -z "`$FILE "$arg_file" | $GREP $ext`" ]; then
$ECHO "$ext"
else
$ECHO "unknown"
fi
}
archive_exists ()
{
# Check archive exist and it readable
arg_arc=$1
# First check archive exists and readable
if [ ! -f "$arg_arc" -a ! -r "$arg_arc" ]; then
$ECHO "ERROR: Archive $arg_arc does not exist"
$ECHO " or you haven't permissions to read."
$ECHO "Exiting..."
exit 1
fi
# Second we'll check archive type
if [ "`archive_type $arg_arc`" = "$ARC_NAME" -a \
"`archive_type $arg_arc`" = " $ext" ]; then
$ECHO "ERROR: Archive $arg_arc it has wrong type."
$ECHO "Exiting..."
exit 1
fi
}
destroy_fs ()
{
# Destroy filesystem(s) recursively
arg_fs=$1
$ZFS destroy -r "$arg_fs" > /dev/null 2>&1
# Check exit code
if [ "`$ECHO $?`" != "0" ]; then
$ECHO "WARNING: Filesystem $arg_fs does not exists."
fi
}
create_snap ()
{
# Create snapshot recursively
arg_filesystem=$1
$ZFS snapshot -r "$arg_filesystem@$SNAP_EXT"
}
zfs_receive ()
{
# Receive filesystem(s) from archive
arg_fs=$1 # Archive or fs to receive
arg_filesys=$2 # Target fs
arg_remote_host=$3 # Remote host
# Verbose output flag set in interactive mode
if [ "$verbose" = "1" ]; then
verb="v"
fi
# When use compression, restore fs with archiver
if [ "$remote_mode" != "1" -a "$remote_file_archive" != "1" ]; then
if [ "$compress" = "1" -a "`archive_type $arg_fs`" = "$ARC_NAME" ]; then
# Send from local file with archiver
$GZIP -d -c $arg_fs | $ZFS receive -dF"$verb" "$arg_filesys"
elif [ "`archive_type $arg_file`" = "$ext" ]; then
# Send from local file without archiver
$ZFS receive -dF"$verb" "$arg_filesys" < "$arg_fs"
fi
elif [ "$remote_mode" = "1" -a "$remote_file_archive" != "1" ]; then
if [ "$compress" = "1" ]; then
# Send to remote from local fs with compression
create_snap $arg_fs
$ZFS send -R$verb $arg_fs@$SNAP_EXT|$GZIP -$COMP_LEVEL -|\
$SSH $arg_remote_host "/bin/gzip -c -d -|/sbin/zfs receive -dF$verb $arg_filesys"
else
# Send to remote from local fs without compression
create_snap $arg_fs
$ZFS send -R$verb $arg_fs@$SNAP_EXT|\
$SSH $arg_remote_host "/sbin/zfs receive -dF$verb $arg_filesys"
fi
# Destroy local and remote snapshots
$SSH $arg_remote_host "/sbin/zfs destroy -r `/sbin/zfs list -H -o name -t snapshot|\
/bin/grep $arg_filesys@$SNAP_EXT`> /dev/null 2>&1"
destroy_fs $arg_fs@$SNAP_EXT
elif [ "$remote_mode" = "1" -a "$remote_file_archive" = "1" -a "`archive_type $arg_fs`" = "$ARC_NAME" ]; then
check_hostname `$ECHO "$arg_fs"|$CUT -f1 -d"."` $arg_remote_host
if [ "$compress" = "1" ]; then
# Send to remote from local archive with compression
$CAT $arg_fs|$SSH $arg_remote_host "/bin/gzip -c -d -|/sbin/zfs receive -dF$verb $arg_filesys"
else
# Send to remote from local archive without compression
$CAT $arg_fs|$SSH $arg_remote_host "/sbin/zfs receive -dF$verb $arg_filesys"
fi
# Destroy remote snapshots
$SSH $arg_remote_host "/sbin/zfs destroy -r `/sbin/zfs list -H -o name -t snapshot|\
/bin/grep $arg_filesys@$SNAP_EXT`> /dev/null 2>&1"
fi
}
##############
# Main block #
##############
# Checking OS
check_os
# Checking root
check_root
# Check command-line arguments
if [ "x$*" = "x" ]; then
# If arguments list empty, show usage note
usage_note
else
arg_list=$*
# Parse command line
set -- `$GETOPT fFrRnNvVhH: $arg_list` || {
usage_note
}
# Read arguments
for i in $arg_list
do
case $i in
-f|-F) remote_file_archive="1";;
-r|-R) remote_mode="1";;
-n|-N) compress_off="1";;
-v|-V) verbose="1";;
-h|-H|\?) usage_note;;
*) shift
archive=$1
filesystem=$2;
remote_host=$3
break;;
esac
shift
done
# Remove trailing --
shift `$EXPR $OPTIND - 1`
fi
# Check filesystem exists
check_fs_exists $filesystem
$ECHO "*** BEGIN: ZFS restore for $filesystem at `$DATE`."
# Check archiver
if [ ! -f "$GZIP" -a ! -x "$GZIP" -o "$compress_off" = "1" ]; then
$ECHO "INFO: Compression will NOT be used. GZip not found or compression disabled."
compress="0"
elif [ -f "$GZIP" -a -x "$GZIP" -a "$compress_off" != "1" ]; then
$ECHO "INFO: Data will be compressed with gzip -$COMP_LEVEL."
compress="1"
fi
# Check archive file exists and correct type when non-remote operation
if [ "$remote_mode" != "1" -o "x$remote_file_archive" != "x" ]; then
# If filesystem not specified, let's get it from file name
if [ "x$filesystem" = "x" ]; then
# Set initial archive file name
# Replase slashes with % if dataset specified in non-remote mode
filesystem=`$ECHO "$archive" | $CUT -f2 -d"." | $SED -e 's/%/\//g' | $CUT -f1 -d"/"`
fi
archive_exists $archive
fi
# First destroy all snapshots recursively
destroy_fs "$filesystem@$SNAP_EXT"
# Restore ZFS pool/dataset
zfs_receive $archive $filesystem $remote_host
# Finally destroy all snapshots recursively
destroy_fs "$filesystem@$SNAP_EXT"
$ECHO "*** DONE: ZFS restore for $filesystem at `$DATE`."
Для чего это было написано:
Восстановление архивов или некомпрессированных стримов
или с удаленного сервера выполняется при помощи
команды zfsrestore . Хотя действия, необходимые для
восстановления, достаточно просты, тем не менее при их
выполнении в спешке возможны ошибки, с целью минимизации
которых и был написан данный пакет.
Команда подобна команде zfsbackup, и также может
выполняться в локальном режиме:
# zfsrestore [-v] [-n ] /mntpt/arc [local_fs]
где первым аргументом (обязательным) указывается имя
компрессированного или некомпрессированного (распакованного)
стрима (возможно с абсолютным или относительным путем),
второй аргумент указывает целевой пул/датасет (пул либо
файловая система должны существовать на момент выполнения
восстановления), в который будет выполняться восстановление.
Второй аргумент (ZFS-пул/датасет) не является обязательным.
Если он опущен, имя целевого пула либо датасета
извлекается из имени файла архива, например, для
архива с именем server5.data%work2.0.zfs.gz имя
пула будет data соответственно.
или вы можете выполнять восстановление в удаленном режиме:
# zfsrestores [-v] [-r] [-f] /mntpt/arc|local_fs \
[remote_fs] [user@]host
Remote restore can be done from local ZFS filesystem (option
-r, backed up previously by zfsbackup or manual zfs send
command) or from compressed or uncompressed (option -n)
archive (o, created by zfsbackup (locally or remote).
Удаленное восстановление может выполняться с локальной
файловой системы ZFS (опция -r, созданной ранее командой
zfsbackup или ручным выполнением команды zfs send) или из
компрессированного либо некомпрессированного (опция -n)
архива (опция -f), созданного zfsbackup (локально либо
удаленно).
В случае, если для восстановления указывается
компрессированный стрим, на целевой машине должен быть
установлен архиватор (GZip), в случае его недоступности
следует распаковать стрим вручную на другой машине
и выполнять восстановление уже с некомпрессированного стрима
(его расширение должно быть .zfs).
При необходимости выборочного восстановления рекомендуется
предварительно создать целевой датасет, в который
и выполнять восстановление стрима. При восстановлении
bare-metal указыватся предварительно воссозданый ZFS-пул
с именем, совпадающим с именем архивированного пула либо
датасета.
Если восстановление выполняется в совпадающую файловую
систему с имеющимися данными (непустую), производится
перезапись существующих данных и дозапись данных,
отсутствующих в целевой файловой системе.
Если при выполнении команды zfsrestore точка
монтирования /usr недоступна, выполнение команды невозможно
и необходимо выполнять
Ручное восстановление
---------------------
Ручное восстановление архивов выполняется в случаях, если
по какой-либо причине невозможно выполнение команды
zfsrestore (система в single-user при корневой системе
на UFS в случае нахождения точки монтирования /usr
на недоступном слайсе, восстановление корневого пула при
загрузке с внешнего носителя итп.).
Процедура ручного восстановления достаточно проста, однако
требует некоторого внимания администратора.
Если целевой ZFS-пул отсутствует, его требуется создать
до начала восстановления данных. Затем следует убедиться,
что пул исправен командой zpool status <целевой пул>.
В случае восстановления данных на произвольный датасет
(с целью выборочного восстановления данных) он также должен
быть создан до начала восстановления.
Пример восстановления целого ZFS-пула:
# zpool create data c0t0d0s6
# gzcat server5.data.0.zfs.gz | zfs receive -vdF data
После подобной операции никаких дополнительных действий
не требуется, восстановление на самый верхний уровень
иерархии выполняется в одно действие.
Пример восстановления датасета:
# gzcat server5.data%work2.0.zfs.gz | zfs receive -vdF data
# zfs destroy -r data/work2@snapshot
При восстановлении датасета целевой пул, в котором
находилась (находится) восстанавливаемая система, также
должен существовать до начала восстановления. После
восстановления датасета необходимо рекурсивно удалить
снапшот верхнего уровня, восстановленный из архива,
поскольку в данном случае он остается после операции zfs
receive в наборе файловых систем.
Следует помнить, что, если при выполнении восстановления
архиватор на целевой машине недоступен, то до начала
восстановления необходимо распаковать компрессированный
стрим на другой машине и выполнять восстановление
посредством несколько видоизмененной команды, пример:
# zfs receive -vdF data < server5.data%work2.0.zfs.gz
Вы также можете выполнить данную процедуру через сеть,
с использованием SSH Или любого другого подходящего сетевого
сервиса для передачи стримов.
Обратите внимание - мы говорим о восстановлении некорневых (non-root) пулов. Восстановление root pool - чуть более сложная задача, требующая несколько большего количества телодвижений:
Восстановление root pool
------------------------
Процедура восстановления root pool в случае, например,
полного отказа системного диска требует несколько большего
внимания администратора и должна выполняться вручную.
При замене поврежденного системного диска выполняется ряд
обязательных процедур, связанных с заменой дисков. Вместе
с тем, при наличии удаленной резервной копии корневого пула
процедура восстановления позволяет сравнительно быстро
привести систему в загружаемое состояние.
Прежде всего, для восстановления корневого пула необходима
его резервная копия - в виде архива (компрессированного или
нет) или сохраненная в виде файловой системы на резервном
сервере.
Последующие действия описаны в виде алгоритма ниже.
1. Необходимо загрузить восстанавливаемую систему с внешнего
носителя - DVD, CD, USB или сети:
ok boot cdrom
или
ok boot net
2. На резервном сервере, содержащем бэкап (архив или
файловую систему) необходимо сконфигурировать удаленный
доступ для R-утилит с восстанавливаемой машины и запустить
соответствующие сервисы:
backup_server# netservices open
backup_server# vi /.rhosts
backup_server# vi /etc/hosts_equiv
При наличии сконфигурированного IPF необходимо разрешить
доступ на сервер восстановления c восстанавливаемой машины.
3. При физическом отказе и замене системного диска новый
диск необходимо пометить и разбить на слайсы. Метка диска
должна иметь тип SMI.
4. На восстанавливаемой машине пересоздается корневой пул на
новом диске, например:
# zpool create -f -o failmode=continue -R /a \
-m legacy rpool c0t0d0s0
5. На новый диск устанавливается загрузчик:
# installboot -F zfs /usr/platform/`uname -i`/lib/fs/zfs/bootblk \
/dev/rdsk/c0t0d0s0
или
# installgrub /boot/grub/stage1 /boot/grub/stage2 \
/dev/rdsk/c0t0d0s0
6. Далее выполняется прием рекурсивного снапшота
и восстановление содержимого пула с удаленного резервного
сервера:
# rsh backup_server zfs send backup_data/rpool|zfs receive -Fdv rpool
либо из компрессированного архива, находящегося на удаленном
резервном сервере:
# rsh backup_server gzip -c -d pegasus.rpool.0.zfs.gz| zfs receive -Fdv rpool
Можно также выполнить восстановление root pool
непосредственно с локальной архивной копии, находящейся,
например, на USB-устройстве (неархивированный стрим, для
компрессированного архива требуется предварительно выполнить
декомпрессию ввиду возможной недоступности gzip в miniroot
загрузочного дистрибутива):
# cd /usbmount
# cat pegasus.rpool.0.zfs|zfs receive -dFv rpool
Операция восстановления содержимого пула может потребовать
некоторого времени.
7. Необходимо убедиться, что корневой пул был успешно
восстановлен в полном объеме командой zfs list.
8. Необходимо установить свойство bootfs корневого пула
в правильное (оригинальное) значение:
# zpool set bootfs=rpool/ROOT/s10x_u8wos_08a rpool
Пересоздание dump и swap разделов не требуется, если
резервная копия корневого пула создавалась при помощи
zfsbackup. Рекурсивный снапшот корневого пула содержит
данные разделы и они будут восстановлены автоматически.
9. Перезагрузить систему с восстановленного пула:
# init 6
Замечание: Вместо пересоздания пула и его восстановления
можно также использовать (начиная с версии 10/09)
функциональность zflash и выполнить восстановление
средствами JumpStart flash install с некомпрессированного
стрима (архива), созданного утилитой zfsbackup.
Однако, ничего невозможного. :)
Что касается совместимости (а мне точно известно, что до сих пор эксплуатируются весьма старые версии Solaris):
Пакет работает на Solaris 10 10/08 и выше и OpenSolaris
начиная с релиза 2008.11.
Пакет поддерживает создание архивов ZFS и их восстановление
начиная с Solaris 10 8/07 (поддерживаются только некорневые
пулы ZFS), начиная с релиза 10/08 поддерживаются также
корневые ZFS-пулы (ZFS root pools).
Техническая поддержка
Автор не оказывает техническую поддержку ни на какой основе. Вы используете предоставленный код на свой собственный страх и риск.
Лицензия
Вы можете модифицировать код как вам желательно, однако обязаны сохранить копирайты первоначального автора.
Скачать
Вы можете скачать вышеописанные утилиты в виде скриптов плюс man-страницы к ним в формате man, плюс процитированные readme с краткими руководствами по применению здесь.