diff --git a/Makefile b/Makefile index 33745aa..9093dea 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ SNAPPER_TEMPLATES ?= /etc/snapper/config-templates DSNAP_SYNC_EXAMPLES = /usr/share/doc/dsnap-sync BIN_DIR = $(DESTDIR)$(PREFIX)/bin +ETC_DIR = $(DESTDIR)/etc SYSTEMD_DIR = $(DESTDIR)$(PREFIX)/lib/systemd/system DOC_DIR = $(DESTDIR)$(PREFIX)/share/doc/$(PKGNAME) @@ -31,5 +32,6 @@ DOC_DIR = $(DESTDIR)$(PREFIX)/share/doc/$(PKGNAME) install: @./find_snapper_config || sed -i 's@^SNAPPER_CONFIG=.*@SNAPPER_CONFIG='$(SNAPPER_CONFIG)'@g' bin/$(PKGNAME) @install -Dm755 bin/* -t $(BIN_DIR)/ + @install -Dm644 etc/* -t $(DESTDIR)/$(ETC_DIR)/ @install -Dm644 $(SNAPPER_TEMPLATES)/* -t $(DESTDIR)/$(SNAPPER_TEMPLATES)/ @install -Dm644 $(DSNAP_SYNC_EXAMPLES)/* -t $(DESTDIR)/$(DOC_DIR)/ diff --git a/bin/tape_admin b/bin/tape_admin new file mode 100755 index 0000000..7c0d9f6 --- /dev/null +++ b/bin/tape_admin @@ -0,0 +1,1075 @@ +#! /bin/dash + +# tape_admin +# https://github.com/rzerres/dsnap-sync +# Copyright (C) 2017, 2018 Ralf Zerres + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., + +# ------------------------------------------------------------------------- + +# Helper routines for tape handling + +progname="${0##*/}" +version="0.0.1" + +# global variables +color=0 +date_cmd="date -u +%Y%M%d%H%M%S" +default_changer_device="/dev/changer" +dryrun=0 +ltfs_mountpoint="/media/tape" +ltfs_devname="/dev/sg9" +mediapools_json="/etc/dsnap-sync/MediaPools.json" +mediapool_name="" +use_mtx=0 +verbose=0 +volume_name="" + +# +MTX=mtx +JQ=jq +LTFS=ltfs +LTFSCK=ltfsck +MKLTFS=mkltfs + +# ascii color +BLUE= +GREEN= +MAGENTA= +RED= +YELLOW= +NO_COLOR= + +### +# functions +### + +check_prerequisites () { + # requested binaries: + which ${MTX} >/dev/null 2>&1 || { printf "'%s' is not installed.\n" ${MTX} && exit 1; } + which ${LTFS} >/dev/null 2>&1 || { printf "'%s' is not installed.\n" ${LTFS} && exit 1; } + which ${MKLTFS} >/dev/null 2>&1 || { printf "'%s' is not installed.\n" ${MKLTFS} && exit 1; } + + which ${JQ} >/dev/null 2>&1 || { printf "'%s' is not installed.\n" ${JQ} && exit 1; } + +} + +compare_date () { + local date1=${1} + local date2=${2} + + if [ $date1 -eq $date2 ]; then + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}date1 ${GREEN}'%s'${MAGENTA} is equal to date2 ${GREEN}'%s'${NO_COLOR}\n" \ + "$date1" "$date2" + fi + return 0 + elif [ $date1 -lt $date2 ]; then + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}date1 ${GREEN}'%s'${MAGENTA} is lower then date2 ${GREEN}'%s'${NO_COLOR}\n" \ + "$date1" "$date2" + fi + return 1 + elif [ $date1 -gt $date2 ]; then + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}date1 ${GREEN}'%s'${MAGENTA} is greater then date2 ${GREEN}'%s'${NO_COLOR}\n" \ + "$date1" "$date2" + fi + return 2 + fi +} + +die () { + error "$@" + exit 1 +} + +error () { + printf "\n==> ERROR: %s\n" "$@" + notify_error 'Error' 'Check journal for more information.' +} >&2 + +get_mediapolicy () { + local mediapools_json=${mediapools:-$mediapools_json} + local mediapool_name=${1} + local volume_name=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}get_mediapolicy...${NO_COLOR}\n" + fi + + if test ! -r ${mediapools_json}; then + if [ $verbose -ge 1 ]; then + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}}\n" \ + "$mediapools_json" + fi + exit 1 + fi + + cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ + | select(.Name == \"${mediapool_name}\") \ + | .Member[] \ + | select(.VolumeName == \"${volume_name}\") \ + | .MediaPolicy ' \ + ${mediapools_json}" + volume_mediapolicy=$(eval $cmd) + + if [ $verbose -ge 3 ]; then + printf "${MAGENTA}MediaPolicy for volume_name ${GREEN}'%s'${MAGENTA} in media-pool ${GREEN}'%s'${MAGENTA}: ${NO_COLOR}%s${NO_COLOR}\n" \ + "$volume_name" "$mediapool_name" "$volume_mediapolicy" + fi +} + +get_poolmember () { + # return: 0-> volume_name match; 1 -> volume_name does not match + local mediapools_json=${mediapools:-$mediapools_json} + local mediapool_name=${1} + local volume_name=${2:-any} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}get_poolmember...${NO_COLOR}\n" + fi + + if test ! -r ${mediapools_json}; then + if [ $verbose -ge 1 ]; then + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}}\n" \ + "$mediapools_json" + fi + exit 1 + fi + + #cmd="jq --monochrome-output --join-output --ascii-output '.MediaPool[] \ + cmd="jq --monochrome-output --ascii-output '.MediaPool[] \ + | select(.Name == \"${mediapool_name}\") \ + | .Member[].VolumeName' \ + ${mediapools_json}" + poolmember=$(eval $cmd) + + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}poolmembers for media-pool ${GREEN}'%s'${MAGENTA} are:\n${NO_COLOR}%s${NO_COLOR}\n" \ + "$mediapool_name" "$poolmember" + fi + + for i in $poolmember ; do + if test "$i" = "\"${volume_name}\""; then + if [ $verbose -ge 3 ]; then + printf "${MAGENTA}volume_name ${GREEN}'%s'${MAGENTA} is member of media-pool ${GREEN}'%s'${NO_COLOR}}\n" \ + "$i" "$mediapool_name" + fi + return 0 + break + fi + done + return 1 +} + +get_poolmember_next () { + local mediapools_json=${mediapools:-$mediapools_json} + local mediapool_name=${1} + local volume_name=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}get_poolmember_next...${NO_COLOR}\n" + fi + + if test ! -r ${mediapools_json}; then + if [ $verbose -ge 1 ]; then + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}}\n" \ + "$mediapools_json" + fi + exit 1 + fi + + cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ + | select(.Name == \"${mediapool_name}\") \ + | .Member[] \ + | .VolumeName ' \ + ${mediapools_json}" + volume_poolmember=$(eval $cmd) + + i=0 + volume_index=-1 + for member in $volume_poolmember; do + if [ $i -eq 0 ]; then + volume_name_next=$(echo $member | sed -e 's/"//g') + fi + if [ $i -eq $volume_index ]; then + if [ ${#member} -ge 1 ]; then + volume_name_next=$(echo $member | sed -e 's/"//g') + break; + fi + fi + if [ $member = \"$volume_name\" ]; then + volume_index=$(($i+1)) + fi + i=$(($i + 1)) + done + + if [ $verbose -ge 3 ]; then + printf "${MAGENTA}poolmember_next for media-pool ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ + "$mediapool_name" "$volume_name_next" + fi +} + +get_slot () { + local mediapools_json=${mediapools:-$mediapools_json} + local mediapool_name=${1} + local volume_name=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}get_slot...${NO_COLOR}\n" + fi + + if test ! -r ${mediapools_json}; then + if [ $verbose -ge 1 ]; then + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}}\n" \ + "$mediapools_json" + fi + exit 1 + fi + + cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ + | select(.Name == \"${mediapool_name}\") \ + | .Member[] \ + | select(.VolumeName == \"${volume_name}\") \ + | .Slot ' \ + ${mediapools_json}" + volume_slot=$(eval $cmd) + + if [ $verbose -ge 3 ]; then + printf "${MAGENTA}Slot for volume_name ${GREEN}'%s'${MAGENTA} from media-pool ${GREEN}'%s'${MAGENTA}: ${NO_COLOR}%s${NO_COLOR}\n" \ + "$volume_name" "$mediapool_name" "$volume_slot" + fi +} + +get_retensiondate () { + local mediapools_json=${mediapools:-$mediapools_json} + local mediapool_name=${1} + local volume_name=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}get_retensiondate...${NO_COLOR}\n" + fi + + if test ! -r ${mediapools_json}; then + if [ $verbose -ge 1 ]; then + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}}\n" \ + "$mediapools_json" + fi + exit 1 + fi + + cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ + | select(.Name == \"${mediapool_name}\") \ + | .Member[] \ + | select(.VolumeName == \"${volume_name}\") \ + | .RetensionDate ' \ + ${mediapools_json}" + volume_retensiondate=$(eval $cmd) + + if [ $verbose -ge 3 ]; then + printf "${MAGENTA}RetensionDate for volume_name ${GREEN}'%s'${MAGENTA} in media-pool ${GREEN}'%s'${MAGENTA}: ${NO_COLOR}%s${NO_COLOR}\n" \ + "$volume_name" "$mediapool_name" "$volume_retensiondate" + fi +} + +ltfs_format () { + local ltfs_devname=${ltfs_devname} + local volume_name=${1} + local tape_id=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}ltfs_format...${NO_COLOR}\n" + fi + + make_err_file + ${MKLTFS} --device=$ltfs_devname --volume-name=${volume_name} --tape-serial=${tape_id} 2>${ERRFILE} + RET=$? +} + +ltfs_is_mounted () { + local ltfs_devname + + if [ $verbose -ge 1 ]; then + printf "${BLUE}ltfs_is_mounted...${NO_COLOR}\n" + fi + + ltfs_devname=$(findmnt -n -T $ltfs_mountpoint -o source | awk -F ':' '{print $2}') + if [ ${#ltfs_devname} -gt 1 ]; then + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}LTFS tape mounted via ${GREEN}'%s'${MAGENTA} at ${GREEN}'%s'${NO_COLOR}\n" \ + "$ltfs_devname" "$ltfs_mountpoint" + fi + return 0 + else + return 1 + fi +} + +ltfs_mount () { + if [ $verbose -ge 1 ]; then + printf "${BLUE}ltfs_mount...${NO_COLOR}\n" + fi + + ltfs_is_mounted + if [ $? -eq 1 ]; then + if [ ! -d $ltfs_mountpoint ]; then + mkdir -p $ltfs_mountpoint + fi + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}LTFS mounting tape ${GREEN}'%s'${MAGENTA} to ${GREEN}'%s'${NO_COLOR}\n" \ + "$ltfs_devname" "$ltfs_mountpoint" + ${LTFS} -o devname=$ltfs_devname -o verbose=$verbose $ltfs_mountpoint + else + ${LTFS} -o devname=$ltfs_devname -o verbose=0 $ltfs_mountpoint + fi + if [ $? -eq 0 ]; then + return 0 + else + return $? + fi + fi +} + +ltfs_umount () { + if [ $verbose -ge 1 ]; then + printf "${BLUE}ltfs_unmount...${NO_COLOR}\n" + + fi + + ltfs_is_mounted + if [ $? -eq 0 ]; then + ret=$(umount $ltfs_mountpoint 2>/dev/null) + if [ $? -eq 0 ]; then + if [ $verbose -ge 2 ]; then + printf "${MAGENTA}LTFS tape ${GREEN}'%s'${MAGENTA} unmounted.${NO_COLOR}\n" \ + "$ltfs_devname" + fi + return 0 + else + return 1 + fi + fi +} + +ltfs_wipe () { + local ltfs_devname=${ltfs_devname} + local volume_name=${1} + local tape_id=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}ltfs_wipe...${NO_COLOR}\n" + fi + + rm -rf $ltfs_mountpoint/* +} + +make_err_file() { + ERRFILE=`mktemp $XDG_RUNTIME_DIR/mtx.err.XXXXXXXXXX` + if test x${ERRFILE} = x; then + ERRFILE="$XDG_RUNTIME_DIR/mtx.err.$$" + if test -f ${ERRFILE}; then + echo "ERROR: Temp file security problem on: ${ERRFILE}" + exit 1 + fi + fi +} + +make_temp_file() { + TMPFILE=`mktemp $XDG_RUNTIME_DIR/mtx.XXXXXXXXXX` + if test x${TMPFILE} = x; then + TMPFILE="$XDG_RUNTIME_DIR/mtx.$$" + if test -f ${TMPFILE}; then + echo "ERROR: Temp file security problem on: ${TMPFILE}" + exit 1 + fi + fi +} + +mtx_exchange () { + local changer_device=${changer_device:-$default_changer_device} + #export changer_device + local slot_source=${1} + local slot_target=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}mtx_exchange...${NO_COLOR}\n" + fi + + make_err_file + ${MTX} -f $changer_device exchange $slot_source $slot_target 2>${ERRFILE} + RET=$? +} + +mtx_getlabel () { + local changer_device=${changer_device:-$default_changer_device} + local slot_source=${slot_source:-99} + export slot_source + + if [ $verbose -ge 1 ]; then + printf "${BLUE}Tape get-label...${NO_COLOR}\n" + fi + + make_temp_file + ${MTX} -f $changer_device status >${TMPFILE} + RET=$? + + case ${slot_source} in + 0) + if [ $verbose -ge 2 ]; then + printf "Calling: ${GREEN}%s${MAGENTA} -f ${GREEN}%s${MAGENTA} %s${NO_COLOR}\n" \ + "${MTX}" "${changer_device}" "${slot_source}" + fi + volume_name=$(perl -ne ' + /Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "$4\n";' ${TMPFILE}) + #/Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "Drive:$1:Slot:$2:$4\n";' ${TMPFILE} + ;; + [0-9][0-8] | [0-9]) + if [ $verbose -ge 2 ]; then + printf "Calling: ${GREEN}%s${MAGENTA} -f ${GREEN}%s${MAGENTA} %s${NO_COLOR}\n" \ + "${MTX}" "${changer_device}" "${slot_source}" + fi + perl -ne ' + /Storage Element ($ENV{"slot_source"}):Full( :VolumeTag=(.+))?/ && print "$3\n";' ${TMPFILE} + ;; + *) + if [ $verbose -ge 2 ]; then + printf "Calling: ${GREEN}%s${MAGENTA} -f ${GREEN}%s${MAGENTA} -- to list all slots${NO_COLOR}\n" \ + "${MTX}" "${changer_device}" + fi + # can be converted to awk+sed+cut, see below + perl -ne ' + /Data Transfer Element (\d+):Empty/ && print "Drive:$1:empty\n"; + /Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "Drive:$1:Slot:$2:$4\n"; + /Storage Element (\d+):Empty/ && print "Slot:$1:empty\n"; + /Storage Element (\d+):Full( :VolumeTag=(.+))?/ && print "Slot:$1:$3\n"; + /Storage Element (\d+) IMPORT.EXPORT:Empty/ && print "Import:$1:empty\n"; + /Storage Element (\d+) IMPORT.EXPORT:Full( :VolumeTag=(.+))?/ && print "Import:$1:$3\n";' ${TMPFILE} + ;; + esac + # If perl isn't installed, you can use by those commands + #cat ${TMPFILE} | grep "Data Transfer Element" | awk "{print \"D:\"\$4 \$7 \$9 \$10}" | sed "s/=/:/" | sed "s/Full/F:/" | sed "s/Empty/E/" + #cat ${TMPFILE} | grep -v "Data Transfer Element" | grep "Storage Element" | grep -v "IMPORT/EXPORT" | awk "{print \"S:\"\$3 \$4 \$5}" | sed "s/IMPORT\/EXPORT//" | sed "s/Full *:VolumeTag=/F:/" | sed "s/Empty/E/" + #cat ${TMPFILE} | grep -v "Data Transfer Element" | grep "Storage Element" | grep "IMPORT/EXPORT" | awk "{print \"I:\"\$3 \$4 \$5}" | sed "s/IMPORT\/EXPORT//" | sed "s/Full *:VolumeTag=/F:/" | sed "s/Empty/E/" + + rm -f ${TMPFILE} >/dev/null 2>&1 + return $RET +} + +mtx_inventory () { + local changer_device=${changer_device:-$default_changer_device} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}mtx_inventory...${NO_COLOR}\n" + fi + + ${MTX} -f $changer_device inventory + RET=$? +} + +mtx_load () { + local changer_device=${changer_device:-$default_changer_device} + local slot_source=${1} + local drive=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}mtx_load...${NO_COLOR}\n" + fi + + make_err_file + ${MTX} -f $changer_device load $slot_source $drive 2>${ERRFILE} + RET=$? +} + +mtx_status () { + local changer_device=${changer_device:-$default_changer_device} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}Tape status...${NO_COLOR}\n" + fi + + ${MTX} -f $changer_device status +} + +mtx_transfer () { + local changer_device=${changer_device:-$default_changer_device} + #export changer_device + local slot_source=${1##slot_source=} + local slot_target=${2##slot_target=} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}Tape transfer...${NO_COLOR}\n" + fi + + make_err_file + ${MTX} -f $changer_device transfer $slot_source $slot_target 2>${ERRFILE} + RET=$? +} + +mtx_unload () { + local changer_device=${changer_device:-$default_changer_device} + local slot_source=${1} + local drive=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}Tape unload...${NO_COLOR}\n" + fi + + make_err_file + for i in 1 2 3 4 5 ; do + ${MTX} -f $changer_device unload $slot_source $drive 2>${ERRFILE} + RET=$? + if test $RET -eq 0; then + break + fi + grep "Error Code=" ${ERRFILE} 2>/dev/null 1>/dev/null + if test $? -ne 0 ; then + break + fi + sleep $i + done + cat ${ERRFILE} + rm -f ${ERRFILE} >/dev/null 2>&1 + if test $RET -ne 0 ; then + if [ $verbose -ge 2 ]; then + printf "${RED}Fail: %s -f %s unload slot=%s drive=%s${NOCOLOR}\n" \ + "${MTX}" "${changer_device}" "${slot_source}" "${drive}" + fi + fi + exit $RET + + +} + +parse_params () { + #printf "\n${BLUE}Parse arguments...${NO_COLOR}\n" + + # Evaluate given call parameters + while [ $# -gt 0 ]; do + key="$1" + case $key in + -h | --help | \-\? | --usage) + # Call usage() function. + usage + ;; + --dry-run|--dryrun) + dryrun=1 + shift 1 + ;; + --get-retensiondate) + shift 1 + pool_params=${*} + pool_params="${pool_params%% -*}" + params=$* + set -- $pool_params + count=$# + test $count -ge 1 && mediapool_name="$1" + test $count -ge 2 && volume_name="$2" + set -- $params + shift $count + cmd=get-retensiondate + ;; + --get-poolmember) + shift 1 + pool_params=${*} + pool_params="${pool_params%% -*}" + params=$* + set -- $pool_params + count=$# + test $count -ge 1 && mediapool_name="$1" + test $count -ge 2 && volume_name="$2" + set -- $params + shift $count + cmd=get-poolmember + ;; + --get-poolmember-next) + shift 1 + pool_params=${*} + pool_params="${pool_params%% -*}" + params=$* + set -- $pool_params + count=$# + test $count -ge 1 && mediapool_name="$1" + test $count -ge 2 && volume_name="$2" + set -- $params + shift $count + cmd=get-poolmember-next + ;; + --get-slot) + shift 1 + pool_params=${*} + pool_params="${pool_params%% -*}" + params=$* + set -- $pool_params + count=$# + test $count -ge 1 && mediapool_name="$1" + test $count -ge 2 && volume_name="$2" + set -- $params + shift $count + cmd=get-slot + ;; + --mount-tape) + shift 1 + tape_params=${*} + tape_params="${tape_params%% -*}" + params=$* + set -- $tape_params + count=$# + test $count -lt 2 && usage + test $count -ge 1 && mediapool_name="$1" + test $count -ge 2 && volume_name="$2" + set -- $params + shift $count + cmd=mount-tape + ;; + -p|--port) + port=$2 + shift 2 + ;; + --remote) + remote=$2 + shift 2 + ;; + --use-mtx) + use_mtx=1 + shift 1 + ;; + --ltfs-devname) + ltfs_devname="$2" + shift 2 + ;; + --ltfs-format) + shift 1 + ltfs_params=${*} + ltfs_params="${ltfs_params%% -*}" + params=$* + set -- $ltfs_params + count=$# + test $count -ge 1 && volume_name="$1" + test $count -ge 2 && tape_id="$2" + set -- $params + shift $count + cmd=ltfs-format + ;; + --ltfs-is-mounted) + shift 1 + cmd=ltfs-is-mounted + ;; + --ltfs-mount) + shift 1 + cmd=ltfs-mount + ;; + --ltfs-mountpoint) + ltfs_mountpoint="$2" + shift 2 + ;; + --ltfs-umount) + shift 1 + cmd=ltfs-umount + ;; + --mtx-exchange) + shift + slots=${*} + slots="${slots%% -*}" + params=$* + set -- $slots + count=$# + test $count -lt 2 && usage + slot_source="$1" + slot_target="$2" + set -- $params + shift $count + cmd=mtx-exchange + ;; + --mtx-getlabel) + shift + slots=${*} + slots="${slots%% -*}" + params=$* + set -- $slots + count=$# + test $count -eq 1 && slot_source="$1" + set -- $params + shift $count + slot_getlabel=1 + cmd=mtx-getlabel + ;; + --mtx-inventory|mtx-inquiry) + shift + cmd=mtx-inventory + ;; + --mtx-load) + shift + slots=${*} + slots="${slots%% -*}" + params=$* + set -- $slots + count=$# + test $count -lt 1 && usage + slot_source="$1" + test $count -eq 2 && drive="$2" + set -- $params + shift $count + cmd=mtx-load + ;; + --mtx-transfer) + shift + slots=${*} + slots="${slots%% -*}" + params=$* + set -- $slots + count=$# + test $count -lt 2 && usage + slot_source="${1}" + slot_target="${2}" + set -- $params + shift $count + cmd=mtx-transfer + ;; + --mtx-unload) + shift + slots=${*} + slots="${slots%% -*}" + params=$* + set -- $slots + count=$# + test $count -lt 1 && usage + slot_source="$1" + test $count -eq 2 && drive="$2" + set -- $params + shift $count + cmd=mtx-unload + ;; + -v|--verbose) + verbose=$(($verbose + 1)) + shift 1 + ;; + --version) + printf "%s v%s\n" "$progname" "$version" + exit 0 + ;; + --ltfs-devname=*) + ltfs_devname="${1#*=}" + shift + ;; + --ltfs-mountpoint=*) + ltfs_mountpoint="${1#*=}" + shift + ;; + --color=*) + case ${1#*=} in + yes | Yes | True | true) + color=1; + ;; + *) + ;; + esac + shift + ;; + --remote=*) + remote=${1#*=} + shift + ;; + --v=* | --verbose=*) + verbose=${1#*=} + shift + ;; + --) # End of all options + shift + break + ;; + -*) + printf "WARN: Unknown option (ignored): $1" >&2 + die "Unknown option" + ;; + *) + printf "Unknown option: %s\nRun '%s -h' for valid options.\n" $key $progname + die "Unknown option" + ;; + esac + done + + if [ -z "$remote" ]; then + ssh="" + else + ssh="ssh -T $remote" + if [ ! -z "$port" ]; then + ssh="$ssh -p $port" + fi + fi + + if [ "$color" ]; then + # ascii color + BLUE='\033[0;34m' + GREEN='\033[0;32m' + MAGENTA='\033[0;35m' + RED='\033[0;31m' + YELLOW='\033[0;33m' + NO_COLOR='\033[0m' + fi + + if [ $verbose -ge 2 ]; then + printf "${BLUE}$progname (runtime arguments)...${NO_COLOR}\n" + printf "LTFS Settings\n" + printf " ltfs device-name: '%s'\n" "$ltfs_devname" + printf " ltfs mount-point: '%s'\n" "$ltfs_mountpoint" + printf "MTX Settings\n" + printf " def changer-name: '%s'\n" "$default_changer_device" + + if [ $verbose -ge 1 ]; then tape_options="verbose_level=$verbose"; fi + if [ $dryrun -eq 1 ]; then tape_options="${tape_options} dry-run=true"; fi + if [ $color -eq 1 ]; then tape_options="${tape_options} color=true"; fi + if [ $use_mtx -eq 1 ]; then tape_options="${tape_options} use-mtx=true"; fi + printf "Options: '%s'\n\n" "${tape_options}" + fi +} + +mount_tape () { + local mediapool_name=${1} + local volume_name=${2} + + if [ $verbose -ge 1 ]; then + printf "${BLUE}mount_tape...${NO_COLOR}\n" + fi + + ltfs_is_mounted + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}LTFS Tape is-mounted: ${GREEN}%d${NO_COLOR}\n" \ + "$?" + fi + # get label from tape in given drive slot + mtx_getlabel 0 + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}Tape Label: ${GREEN}%s${NO_COLOR}\n" \ + "${volume_name}" + fi + # check if given tape is poolmember of selected pool + get_poolmember ${mediapool_name} ${volume_name} + if [ $? -eq 0 ]; then + # volume_name match + get_mediapolicy ${mediapool_name} ${volume_name} + if [ ${#volume_mediapolicy} -gt 0 ]; then + if [ ${volume_mediapolicy} = "append" ] || [ ${volume_mediapolicy} = "overwrite" ] ; then + return 0 + fi + if [ ${volume_mediapolicy} = "overwrite" ] ; then + volume_retensiondate="20180101000000" + get_retensiondate ${mediapool_name} ${volume_name} + date_now=$($date_cmd) + compare_date $date_now $volume_retensiondate + if [ $? -eq 2 ]; then + # retensiondate has exposed: wipe given tape + ltfs_wipe + return 0 + fi + if [ $? -eq 1 ]; then + # respect active retensiondate: unload given tape + ltfs_unmount + mtx_unload + # use next volume_name from pool + get_poolmember_next ${mediapool_name} ${volume_name} + get_slot ${mediapool_name} ${volume_name_next} + mtx_load ${volume_slot} + ltfs_mount + return 0 + fi + fi + fi + else + # volume_name does not match + ltfs_umount + mtx_unload 0 + # TODO + get_poolmember + get_poolmember_next ${mediapool_name} ${volume_name} + #mtx load ${volume_name_next} + fi + else + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}No LTFS Tape mounted...${NO_COLOR}\n" + fi + get_slot ${mediapool_name} ${volume_name} + mtx_load ${volume_slot} + ltfs_mount + return 0 + fi +} + +traperror () { + printf "Exited due to error on line %s.\n" $1 + printf "exit status: %s\n" "$2" + #printf "command: %s\n" "$3" + #printf "bash line: %s\n" "$4" + #printf "function name: %s\n" "$5" + exit 1 +} + +trapkill () { + printf "Exited due to user intervention.\n" + #run_cleanup + exit 0 +} + +usage () { + cat < Send the snapshot backup to a remote machine. The snapshot will be sent via ssh. + You should specify the remote machine's hostname or ip address. The 'root' user + must be permitted to login on the remote machine. + -p, --port The remote port. + --dry-run perform a trial run where no changes are made. + -v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3) + --version show program version +EOF + + exit 0 +} + +### +# Main +### + +cwd=`pwd` + +# can't be ported to dash (ERR is not supported) +trap trapkill TERM INT + +check_prerequisites + +# validate commandline options, set resonable defaults +parse_params $@ + +case $cmd in + get-mediapolicy) + get_mediapolicy "${mediapool_name}" "${volume_name}" + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}MediaPolicy for ${GREEN}'%s'${MAGENTA} is:${NO_COLOR} %s.\n" \ + "${volume_name}" "${volume_mediapolicy}" + fi + fi + ;; + get-poolmember) + valid_member=0 + get_poolmember "${mediapool_name}" "${volume_name}" + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}Volume-Name ${GREEN}'%s'${MAGENTA} is a valid mediapool member.${NO_COLOR}\n" \ + "${volume_name}" + fi + valid_member=1 + fi + ;; + get-poolmember-next) + valid_member=0 + get_poolmember_next "${mediapool_name}" "${volume_name}" + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}Next Volume-Name from media-pool ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}'%s'\n" \ + "${mediapool_name}" "${volume_name_next}" + fi + fi + ;; + get-retensiondate) + get_retensiondate "${mediapool_name}" "${volume_name}" + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}RetensionDate for ${GREEN}'%s'${MAGENTA} is:${NO_COLOR} %s.\n" \ + "${volume_name}" "${volume_retensiondate}" + fi + fi + ;; + get-slot) + get_slot "${mediapool_name}" "${volume_name}" + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}Slot location for ${GREEN}'%s'${MAGENTA} is:${NO_COLOR} %s.\n" \ + "${volume_name}" "${volume_slot}" + fi + fi + ;; + ltfs-format) + ltfs_format "${volume_name}" "${tape_id}" + ;; + ltfs-is-mounted) + ltfs_is_mounted + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}LTFS Tape is-mounted: ${GREEN}%d${NO_COLOR}\n" \ + "$?" + fi + fi + ;; + ltfs-mount) + ltfs_mount + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}LTFS Tape mount: ${GREEN}%d${NO_COLOR}\n" \ + "$?" + fi + fi + ;; + ltfs-umount) + ltfs_umount + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}LTFS Tape unmount: ${GREEN}%d${NO_COLOR}\n" \ + "$?" + fi + fi + ;; + mtx-exchange) + mtx_exchange "${slot_source}" "${slot_target}" + ;; + mtx-getlabel) + mtx_getlabel $slot_source + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}Tape Label: ${GREEN}%s${NO_COLOR}\n" \ + "${volume_name}" + fi + ;; + mtx-inventory) + mtx_inventory + ;; + mtx-load) + mtx_load "${slot_source}" "${drive}" + ;; + mtx-transfer) + mtx_transfer "${slot_source}" "${slot_target}" + ;; + mtx-unload) + mtx_unload "${slot_source}" "${drive}" + ;; + mount-tape) + mount_tape "${mediapool_name}" "${volume_name}" + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}mount tape ${GREEN}'%s'${MAGENTA} for ${GREEN}'%s'${MAGENTA}:${NO_COLOR} %s.\n" \ + "${volume_name}" "${mediapool_name}" "$?" + fi + fi + ;; +esac + +# return: 0 = equal, 1 = lower, 2 = greater +#compare_date "20180824090000" "20180824090000" + +exit 0 diff --git a/etc/dsnap-sync/MediaPools.json b/etc/dsnap-sync/MediaPools.json new file mode 100644 index 0000000..6fe9657 --- /dev/null +++ b/etc/dsnap-sync/MediaPools.json @@ -0,0 +1,32 @@ +{ + "MediaPools": "Daywalker-Pools", + "MediaPool": [ + { + "_comment": "MediaPool append", + "Name": "Pool 1", + "Member":[ + { "VolumeName": "Tape-0001", "VolumeId": "000001", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0002", "VolumeId": "000002", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0003", "VolumeId": "000003", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0004", "VolumeId": "000004", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0005", "VolumeId": "000005", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0006", "VolumeId": "000006", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0007", "VolumeId": "000007", "MediaPolicy": "append" }, + { "VolumeName": "Tape-0008", "VolumeId": "000008", "MediaPolicy": "append" } + ] + }, + { + "_comment": "MediaPool overwrite", + "Name": "Pool 2", + "Member":[ + { "VolumeName": "Tape-0009", "VolumeId": "000001", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" }, + { "VolumeName": "Tape-0010", "VolumeId": "000002", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" }, + { "VolumeName": "Tape-0011", "VolumeId": "000003", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" }, + { "VolumeName": "Tape-0012", "VolumeId": "000004", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" }, + { "VolumeName": "Tape-0013", "VolumeId": "000005", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" }, + { "VolumeName": "Tape-0014", "VolumeId": "000006", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" }, + { "VolumeName": "Tape-0015", "VolumeId": "000007", "MediaPolicy": "overwrite", "RetensionDate": "20182324T000000", "LastWrite": "20182224T09000" } + ] + } + ] +}