#! /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.9" # global variables color=0 date_cmd="date -u +%Y%m%d%H%M%S" # use universial time date_iso="" date_seconds="" default_changer_device="/dev/changer" dryrun=0 ltfs_mountpoint="/media/tape" ltfs_devname="/dev/sg9" mediapools_json="/etc/dsnap-sync/MediaPools.json" mediapool_name="" timezone="Europe/Berlin" use_mtx=0 verbose=0 volume_name="" volume_name_active="" volume_name_next="" # helper programms ATTR=attr JQ=jq LTFS=ltfs LTFSCK=ltfsck MKLTFS=mkltfs MTX=mtx SSH=ssh # ascii color BLUE= GREEN= MAGENTA= RED= YELLOW= NO_COLOR= ### # functions ### check_prerequisites () { # requested binaries: which ${ATTR} >/dev/null 2>&1 || { printf "'%s' is not installed.\n" ${ATTR} && exit 1; } 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 findmnt >/dev/null 2>&1 || { printf "'findmnt' is not installed." && 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; } which perl >/dev/null 2>&1 || { printf "'%s' is not installed.\n" perl && exit 1; } which awk >/dev/null 2>&1 || { printf "'%s' is not installed.\n" awk && exit 1; } which sed >/dev/null 2>&1 || { printf "'%s' is not installed.\n" sed && 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 } date_iso_to_seconds () { local date_string=$1 year=$(echo $date_string | cut -c 1-4) month=$(echo $date_string | cut -c 5-6) day=$(echo $date_string | cut -c 7-8) hour=$(echo $date_string | cut -c 9-10) minute=$(echo $date_string | cut -c 11-12) second=$(echo $date_string | cut -c 13-14) test ${month#0} -gt 12 && printf "${RED}Error:${NO_COLOR} Invalid argument month: '%s'\n" $month && return 1 test ${day#0} -gt 31 && printf "${RED}Error:${NO_COLOR} Invalid argument day: '%s'\n" $day && return 1 test ${hour#0} -gt 23 && printf "${RED}Error:${NO_COLOR} Invalid argument hour: '%s'\n" $hour && return 1 test ${minute#0} -gt 59 && printf "${RED}Error:${NO_COLOR} Invalid argument minute: '%s'\n" $minute && return 1 test ${second#0} -gt 50 && printf "${RED}Error:${NO_COLOR} Invalid argument second: '%s'\n" $second && return 1 date_iso=$(printf "%s-%s-%s %s:%s:%s\n" \ ${year} ${month} ${day} ${hour} ${minute} ${second}) date_seconds=$(date --date="$date_iso" +"%s") } date_seconds_to_iso () { local date_string=$1 date_iso=$(date --date="@$date_string" +"%Y-%m-%d %H:%M:%S") } date_seconds_to_string () { local date_string=$1 date_iso=$(date --date="@$date_string" +"%Y%m%d%H%M%S") } date_string_to_iso () { local date_string=$1 year=$(echo $date_string | cut -c 1-4) month=$(echo $date_string | cut -c 5-6) day=$(echo $date_string | cut -c 7-8) hour=$(echo $date_string | cut -c 9-10) minute=$(echo $date_string | cut -c 11-12) second=$(echo $date_string | cut -c 13-14) test ${month#0} -gt 12 && printf "${RED}Error:${NO_COLOR} Invalid argument month: '%s'\n" $month && return 1 test ${day#0} -gt 31 && printf "${RED}Error:${NO_COLOR} Invalid argument day: '%s'\n" $day && return 1 test ${hour#0} -gt 23 && printf "${RED}Error:${NO_COLOR} Invalid argument hour: '%s'\n" $hour && return 1 test ${minute#0} -gt 59 && printf "${RED}Error:${NO_COLOR} Invalid argument minute: '%s'\n" $minute && return 1 test ${second#0} -gt 59 && printf "${RED}Error:${NO_COLOR} Invalid argument second: '%s'\n" $second && return 1 date_iso=$(printf "%s-%s-%s %s:%s:%s\n" \ ${year} ${month} ${day} ${hour} ${minute} ${second}) } die () { error "$@" exit 1 } error () { printf "\n==> ERROR: %s\n" "$@" notify_error 'Error' 'Check journal for more information.' } >&2 get_lastwrite () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1} #local volume_name=${2} if [ $verbose -ge 1 ]; then printf "${BLUE}get_lastwrite...${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 if [ ${#volume_name} -ge 1 ]; then # select last write date for given volume cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ | .Member[] \ | select(.VolumeName == \"${volume_name}\") \ | .LastWrite ' \ ${mediapools_json}" else # select volume with latest write date cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ | .Member \ | map({ \"VolumeName\" : .VolumeName, \"LastWrite\" : ( .LastWrite | scan(\"[0-9]{14}\")) }) \ | sort_by(.LastWrite) \ | .[-1] \ | .VolumeName ' \ ${mediapools_json}" volume_name=$(eval $cmd) volume_name=$(echo $volume_name | sed -e 's/"//g') cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ | .Member \ | map({ \"VolumeName\" : .VolumeName, \"LastWrite\" : ( .LastWrite | scan(\"[0-9]{14}\")) }) \ | sort_by(.LastWrite) \ | .[-1] \ | .LastWrite ' \ ${mediapools_json}" fi volume_lastwrite=$(eval $cmd) if [ ${#volume_lastwrite} -gt 1 ]; then volume_lastwrite=$(echo $volume_lastwrite | sed -e 's/"//g') if [ $verbose -ge 3 ]; then printf "${MAGENTA}volume_lastwrite for volume ${GREEN}'%s'${MAGENTA} in media-pool ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ "$volume_name" "$mediapool_name" "$volume_lastwrite" fi else return 1 fi } 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) volume_mediapolicy=$(echo $volume_mediapolicy | sed -e 's/"//g') 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_mediapool_name () { local mediapools_json=${mediapools:-$mediapools_json} local volume_name=${1} if [ $verbose -ge 1 ]; then printf "${BLUE}get_mediapool_name...${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[] \ | .Name as \$MediaPoolName \ | .Member[].VolumeName \ | select(. == \"${volume_name}\") \ | \$MediaPoolName' ${mediapools_json}" mediapool_name=$(eval $cmd) mediapool_name=$(echo $mediapool_name | sed -e 's/"//g') if [ $verbose -ge 3 ]; then printf "${MAGENTA}Media-pool name for VolumeName %s: ${GREEN}'%s'${NO_COLOR}\n" \ "$volume_name" "$mediapool_name" fi } get_mediapool_names () { local mediapools_json=${mediapools:-$mediapools_json} #local mediapool_name=${1} if [ $verbose -ge 1 ]; then printf "${BLUE}get_mediapools...${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[] \ | .Name ' \ ${mediapools_json}" mediapool_names=$(eval $cmd) mediapool_names=$(echo $mediapool_names | sed -e 's/"//g') if [ $verbose -ge 3 ]; then printf "${MAGENTA}Media-pool names: ${GREEN}'%s'${NO_COLOR}\n" \ "$mediapool_names" fi } get_mediapool_retensiondays () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1} if [ $verbose -ge 1 ]; then printf "${BLUE}get_mediapool_retensiondays...${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 if [ ${#mediapool_name} -ge 1 ]; then # select default retension days for given pool cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ | .DefaultRetensionDays ' \ ${mediapools_json}" fi mediapool_defaultretensiondays=$(eval $cmd) mediapool_defaultretensiondays=$(echo $mediapool_defaultretensiondays | sed -e 's/"//g') if [ ${#mediapool_defaultretensiondays} -ge 1 ]; then if [ $verbose -ge 3 ]; then printf "${MAGENTA}default retension days for media-pool ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ "$mediapool_name" "$mediapool_defaultretensiondays" fi else if [ $verbose -ge 1 ]; then printf "${RED}Error: ${MAGENTA}No retension days for media-pool ${GREEN}'%s'${NO_COLOR}\n" \ "$mediapool_name" "$mediapool_defaultretensiondays" fi return 1 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 --ascii-output '.MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ | .Member[].VolumeName' \ ${mediapools_json}" poolmember=$(eval $cmd) poolmember=$(echo $poolmember | sed -e 's/"//g') 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:-"none"} 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 \ | map({ \"VolumeName\" : .VolumeName, \"LastWrite\" : ( .LastWrite | scan(\"[0-9]{14}\")) }) \ | sort_by(.LastWrite) \ | .[0] \ | .VolumeName ' \ ${mediapools_json}" volume_poolmember=$(eval $cmd) volume_poolmember=$(echo $volume_poolmember | sed -e 's/"//g') 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} -ge 1 ]; then if [ $member = $volume_name ]; then volume_index=$(($i+1)) fi 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) volume_slot=$(echo $volume_slot | sed -e 's/"//g') 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 [ ${#volume_retensiondate} -gt 1 ]; then volume_retensiondate=$(echo $volume_retensiondate | sed -e 's/"//g') if [ $verbose -ge 3 ]; then printf "${MAGENTA}RetensionDate in UTC 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 else return 1 fi } ltfs_get_attribute () { local ltfs_devname=${ltfs_devname} local ltfs_attribute_name=${1:-volumeName} if [ $verbose -ge 1 ]; then printf "${BLUE}ltfs_get_attribute...${NO_COLOR}\n" fi make_err_file # Reference: https://www.ibm.com/support/knowledgecenter/en/STQNYL_2.4.0/ltfs_hints_virtual_extended_attribute.html # eg: ltfs.indexCreator # ltfs.volumeBlocksize # ltfs.volumeName, ltfs.volumeSerial, ltfs.volumeUUID ltfs_attribute_value=$(${ATTR} -g ltfs.$ltfs_attribute_name $ltfs_mountpoint 2>${ERRFILE}) rm -f ${ERRFILE} if [ $? -eq 0 ]; then ltfs_attribute_value=$(echo ${ltfs_attribute_value##*:} | sed -e 's/\r\n//g') if [ $verbose -ge 2 ]; then printf "${MAGENTA}LTFS attribute ${GREEN}'%s'${MAGENTA} is: ${GREEN}'%s'${NO_COLOR}\n" \ "$ltfs_attribute_name" "$ltfs_attribute_value" fi return 0 else if [ $verbose -ge 2 ]; then printf "${MAGENTA}LTFS attribute ${GREEN}'%s'${MAGENTA} isn't valid${NO_COLOR}\n" \ "$ltfs_attribute_name" fi fi return $? } ltfs_format () { local ltfs_devname=${ltfs_devname} local volume_name=${1} local volume_serial=${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=${volume_serial} 2>${ERRFILE} RET=$? rm -f ${ERRFILE} return $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 is 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 make_err_file 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 1>/dev/null 2>${ERRFILE} else ${LTFS} -o devname=$ltfs_devname -o verbose=0 $ltfs_mountpoint 1>/dev/null 2>${ERRFILE} fi if [ $? -eq 0 ]; then return 0 else # format tape if needed if [ -z $volume_name_active ]; then slot_source="0" mtx_getlabel $slot_source fi need_format=$(grep "medium is not partitioned" ${ERRFILE}) rm -f ${ERRFILE} if [ ${#need_format} -ge 1 ]; then tape_id=$(echo $volume_name_active | sed -e 's/\([[:alpha:]]*-\)//g') # TODO: tape_id needs to be exactly 6 character long volume_serial=$(printf "%0.s0" $(seq 1 $((6 - ${#tape_id})))) volume_serial="${volume_serial}${tape_id}" ltfs_format ${volume_name_active} ${volume_serial} ltfs_mount fi return $? fi else if [ $verbose -ge 2 ]; then printf "${MAGENTA}LTFS tape ${GREEN}'%s'${MAGENTA} is mounted to ${GREEN}'%s'${NO_COLOR}\n" \ "$ltfs_devname" "$ltfs_mountpoint" fi fi } ltfs_reformat () { local volume_name=${1} if [ $verbose -ge 1 ]; then printf "${BLUE}ltfs_reformat...${NO_COLOR}\n" fi ltfs_is_mounted if [ $? -eq 0 ]; then if [ $verbose -ge 2 ]; then printf "${MAGENTA}LTFS tape ${GREEN}'%s'${MAGENTA} is mounted to ${GREEN}'%s'${NO_COLOR}\n" \ "$ltfs_devname" "$ltfs_mountpoint" fi slot_source="0" mtx_getlabel $slot_source if [ ${#volume_name} -gt 0 ] && [ $volume_name_active != $volume_name ]; then get_mediapool_name $volume_name if [ $? -eq 0 ]; then ltfs_umount if [ $? -eq 0 ]; then mtx_unload if [ $? -eq 0 ]; then mount_tape "${mediapool_name}" "${volume_name}" fi fi fi fi else if [ ! -d $ltfs_mountpoint ]; then mkdir -p $ltfs_mountpoint fi make_err_file 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 1>/dev/null 2>${ERRFILE} else ${LTFS} -o devname=$ltfs_devname -o verbose=0 $ltfs_mountpoint 1>/dev/null 2>${ERRFILE} fi if [ $? -ge 0 ]; then if [ -z $volume_name_active ]; then slot_source="0" mtx_getlabel $slot_source fi need_format=$(grep "medium is not partitioned" ${ERRFILE}) rm -f ${ERRFILE} fi fi if [ ${#volume_name} -eq 0 ] || [ $volume_name_active = $volume_name ]; then tape_id=$(echo $volume_name_active | sed -e 's/\([[:alpha:]]*-\)//g') # tape_id needs to be exactly 6 character long volume_serial=$(printf "%0.s0" $(seq 1 $((6 - ${#tape_id})))) volume_serial="${volume_serial}${tape_id}" if [ ${#need_format} -ge 1 ]; then ltfs_format ${volume_name_active} ${volume_serial} else ltfs_wipe ltfs_format ${volume_name_active} ${volume_serial} fi ltfs_mount fi return $? } ltfs_umount () { if [ $verbose -ge 1 ]; then printf "${BLUE}ltfs_umount...${NO_COLOR}\n" fi ltfs_is_mounted if [ $? -eq 0 ]; then if [ $verbose -ge 3 ]; then printf "${MAGENTA}unmounting LTFS tape ${GREEN}'%s'${NO_COLOR}.\n" \ "$ltfs_mountpoint" fi 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_mountpoint" 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 } media_change () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1} local volume_name=${2} if [ $verbose -ge 1 ]; then printf "${BLUE}media_change...${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 if [ ${#mediapool_name } -ge 1 ]; then if [ ${#volume_name } -eq 0 ]; then get_lastwrite "${mediapool_name}" fi add_retensiondays_to_retensiondate "${mediapool_name}" "${volume_name}" "${retension_days}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then date_string_to_iso "${volume_retensiondate}" if [ $? -eq 0 ]; then volume_date=$(date --date="${date_iso}Z") printf "${MAGENTA}Updated RetensionDate date for ${GREEN}'%s'${MAGENTA} to: ${NO_COLOR}'%s'.\n" \ "${volume_name}" "${volume_date}" fi fi fi get_poolmember_next "${mediapool_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}MediaPool: ${GREEN}%s${NO_COLOR} (next tape: ${GREEN}%s${NO_COLOR})\n" \ "${mediapool_name}" "${volume_name_next}" fi update_lastwrite "${mediapool_name}" "${volume_name_next}" "${date_string}" fi fi } mount_tape () { local mediapool_name=${1} local volume_name=${2} if [ $verbose -ge 1 ]; then printf "${BLUE}mount_tape...${NO_COLOR}\n" fi if [ $verbose -ge 2 ]; then if [ ${#volume_name } -ge 1 ]; then printf "${MAGENTA}MediaPool: ${GREEN}%s${NO_COLOR} (mounting tape: ${GREEN}%s${NO_COLOR})\n" \ "${mediapool_name}" "${volume_name}" else printf "${MAGENTA}MediaPool: ${GREEN}%s${NO_COLOR} (mounting next tape)\n" \ "${mediapool_name}" fi fi ltfs_is_mounted if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}LTFS Tape is mounted.${NO_COLOR}\n" fi # get date from last written tape in given mediapool if [ "${#volume_name}" -eq 0 ]; then get_lastwrite ${mediapool_name} if [ $? != 0 ]; then exit 1 fi fi # get label from tape in given drive slot slot_source="0" mtx_getlabel $slot_source if [ $? -eq 0 ]; then if [ $verbose -ge 2 ]; then printf "${MAGENTA}Tape in drive: ${GREEN}%s${NO_COLOR}\n" \ "${volume_name_active}" fi if [ "${#volume_name_active}" -ge 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Re-Mounting Tape: ${GREEN}%s${NO_COLOR}\n" \ "${volume_name}" fi ltfs_umount if [ $? -eq 0 ]; then mtx_unload if [ $? -eq 0 ]; then mount_tape "${mediapool_name}" "${volume_name}" return 0 else return $? fi else return $? fi fi fi # check if given tape is poolmember of selected pool get_poolmember ${mediapool_name} ${volume_name} if [ $? -eq 0 ]; then # volume_name is member of given mediapool get_mediapolicy ${mediapool_name} ${volume_name_active} if [ ${#volume_mediapolicy} -gt 0 ]; then if [ ${volume_mediapolicy} = "append" ] ; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \ "${volume_mediapolicy}" fi volume_retensiondate="20180101000000" get_retensiondate ${mediapool_name} ${volume_name} if [ $? -eq 0 ]; then date_now=$($date_cmd) compare_date $date_now $volume_retensiondate if [ $? -eq 2 ]; then # retensiondate has exposed: use it return 0 fi if [ $? -eq 1 ]; then # respect active retensiondate: unload given tape ltfs_umount 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 if [ $? -eq 0 ]; then return 0 fi fi 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 if [ $? -eq 0 ]; then return 0 fi fi if [ $? -eq 1 ]; then # respect active retensiondate: unload given tape ltfs_umount 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 if [ $? -eq 0 ]; then return 0 fi fi fi fi fi else # volume_name is not member of given pool ltfs_umount if [ $? -eq 0 ]; then mtx_unload if [ $? -eq 0 ]; then if [ "${#volume_name}" -eq 0 ]; then get_lastwrite ${mediapool_name} fi get_poolmember_next ${mediapool_name} ${volume_name} if [ $? -eq 0 ]; then mtx load ${volume_name_next} ltfs_mount fi else return $? fi else return $? fi fi else if [ $verbose -ge 1 ]; then printf "${MAGENTA}No LTFS Tape mounted...${NO_COLOR}\n" fi if [ "${#volume_name}" -eq 0 ]; then # set volume_name and last write date #get_lastwrite ${mediapool_name} get_poolmember_next ${mediapool_name} if [ "${#volume_name_next}" -ge 1 ]; then volume_name=${volume_name_next} fi fi # check label of tape in given drive slot (slot=0) slot_source="0" mtx_getlabel $slot_source if [ $? -eq 0 ] && [ "${#volume_name_active}" -gt 1 ]; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Tape in drive: ${GREEN}%s${NO_COLOR}\n" \ "${volume_name_active}" fi if [ "${#volume_name_active}" -gt 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then mtx_unload if [ $? -eq 0 ]; then mount_tape "${mediapool_name}" "${volume_name}" return 0 else return $? fi fi else # mount new tape get_slot ${mediapool_name} ${volume_name} mtx_load ${volume_slot} fi # volume_name is member of given mediapool get_mediapolicy ${mediapool_name} ${volume_name} if [ ${#volume_mediapolicy} -gt 0 ]; then if [ ${volume_mediapolicy} = "append" ] ; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \ "${volume_mediapolicy}" fi volume_retensiondate="20180101000000" get_retensiondate ${mediapool_name} ${volume_name} date_now=$($date_cmd) compare_date $date_now $volume_retensiondate RET=$? if [ $RET -eq 2 ]; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}RetensionDate has exposed, mount the tape\n" fi ltfs_mount return 0 elif [ $RET -eq 1 ]; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Tape is bocked via RetensionDate.\n" fi ltfs_mount return 0 fi 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_mount if [ $? -eq 0 ]; then ltfs_wipe if [ $? -eq 0 ]; then return 0 fi fi fi if [ $? -eq 1 ]; then # respect active retensiondate: unload given tape mtx_unload if [ $? -eq 0 ]; then # use next volume_name from pool get_poolmember_next ${mediapool_name} ${volume_name} mount_tape "${mediapool_name}" "${volume_name}" return 0 else return $? fi fi fi 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=$? rm -f ${ERRFILE} } 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}mtx_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_active=$(perl -ne ' /Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "$4\n";' ${TMPFILE}) volume_name_active=$(echo $volume_name_active | sed -e 's/ *$//g') ;; [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=$? rm -f ${ERRFILE} } 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=$? rm -f ${ERRFILE} } 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 ${MTX} -f $changer_device unload $slot_source $drive 2>${ERRFILE} for i in 1 2 3 4 5 ; do 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${NO_COLOR}\n" \ "${MTX}" "${changer_device}" "${slot_source}" "${drive}" fi fi return $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 ;; --add-retensiondays) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" params=$* set -- $pool_params count=$# test $count -lt 2 && usage mediapool_name="$1" volume_name="$2" test $count -ge 3 && retension_days="$3" set -- $params shift $count cmd=add-retensiondays ;; --dry-run|--dryrun) dryrun=1 shift 1 ;; --get-lastwrite) 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-lastwrite ;; --get-mediapool-name) volume_name=$2 shift 2 cmd=get-mediapool-name ;; --get-mediapools) shift 1 cmd=get-mediapools ;; --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-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-retensiondate) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" params=$* set -- $pool_params count=$# test $count -ge 1 && mediapool_name="$1" set -- $params shift $count cmd=get-retensiondays ;; --get-retensiondays) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" params=$* set -- $pool_params count=$# test $count -lt 1 && usage mediapool_name="$1" set -- $params shift $count cmd=get-mediapool-retensiondays ;; --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 ;; --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-reformat) volume_name="$2" shift 2 cmd=ltfs-reformat ;; --ltfs-umount) shift 1 cmd=ltfs-umount ;; --media-change) shift 1 tape_params=${*} tape_params="${tape_params%% -*}" params=$* set -- $tape_params count=$# test $count -lt 1 && usage mediapool_name="$1" test $count -ge 2 && volume_name="$2" set -- $params shift $count cmd=media-change ;; --mount) shift 1 tape_params=${*} tape_params="${tape_params%% -*}" params=$* set -- $tape_params count=$# test $count -lt 1 && usage mediapool_name="$1" test $count -ge 2 && volume_name="$2" set -- $params shift $count cmd=mount ;; --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 ;; -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-getattribute) shift 1 ltfs_params=${*} ltfs_params="${ltfs_params%% -*}" params=$* set -- $ltfs_params count=$# test $count -eq 0 && ltfs_attribute_name="volumeName" shift $count cmd=ltfs-getattribute ;; --update-lastwrite) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" params=$* set -- $pool_params count=$# test $count -lt 2 && usage mediapool_name="$1" volume_name="$2" test $count -ge 3 && date_string="$3" set -- $params shift $count cmd=update-lastwrite ;; --update-retensiondate) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" params=$* set -- $pool_params count=$# test $count -lt 2 && usage mediapool_name="$1" volume_name="$2" test $count -ge 3 && date_string="$3" set -- $params shift $count cmd=update-retensiondate ;; --update-retensiondays) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" params=$* set -- $pool_params count=$# test $count -lt 2 && usage mediapool_name="$1" mediapool_defaultretensiondays="$2" set -- $params shift $count cmd=update-mediapool-retensiondays ;; -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 $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 [ ${#remote} -ge 1 ]; then printf "Remote Settings\n" printf " remote host: '%s'\n" "$remote" printf " ssh options: '%s'\n" "$ssh" fi 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 } 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 } update_lastwrite () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1:-0} local volume_name=${2} local date_string=${3:-now} if [ $verbose -ge 1 ]; then printf "${BLUE}update_lastwrite...${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 else json_path=${mediapools_json%/*} json_file=${mediapools_json##*/} cp "$mediapools_json" $XDG_RUNTIME_DIR/$json_file fi if [ ${#volume_name} -ge 1 ]; then # update last write date for given volume (date field in utc) if [ "${date_string}" = "now" ]; then volume_lastwrite=$($date_cmd) else volume_lastwrite=$date_string fi cmd="jq --monochrome-output --ascii-output '.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .Member \ | map(.VolumeName == \"$volume_name\") | index(true)' \ ${mediapools_json}" tape_index=$(eval $cmd) if [ $? -eq 0 ]; then cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .Member[$tape_index] \ | .LastWrite) \ |= \"$volume_lastwrite\" ' \ ${mediapools_json} > $XDG_RUNTIME_DIR/$json_file" $(eval $cmd) if [ $? -eq 0 ]; then cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json" else return 1 fi else return 1 fi if [ $verbose -ge 3 ]; then printf "${MAGENTA}LastWrite in UTC for for volume ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ "$volume_name" "$volume_lastwrite" fi fi } update_mediapool_retensiondays () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1:-0} local mediapool_defaultretensiondays=${2} if [ $verbose -ge 1 ]; then printf "${BLUE}update_mediapool_retensiondays..${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 else json_path=${mediapools_json%/*} json_file=${mediapools_json##*/} cp "$mediapools_json" $XDG_RUNTIME_DIR/$json_file fi if [ ${mediapool_defaultretensiondays} -ge 0 ]; then cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .DefaultRetensionDays) \ |= \"${mediapool_defaultretensiondays}\" ' \ ${mediapools_json} > $XDG_RUNTIME_DIR/$json_file" $(eval $cmd) if [ $? -eq 0 ]; then cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json" else return 1 fi else return 1 fi } add_retensiondays_to_retensiondate () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1:-0} local volume_name=${2} local retension_days=${3:-0} if [ $verbose -ge 1 ]; then printf "${BLUE}add_retensiondays_to_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 else json_path=${mediapools_json%/*} json_file=${mediapools_json##*/} cp "$mediapools_json" $XDG_RUNTIME_DIR/$json_file fi if [ $retension_days -eq 0 ]; then # get default retensiondays form given mediapool get_mediapool_retensiondays $mediapool_name if [ $? -eq 0 ]; then retension_days=${mediapool_defaultretensiondays} fi fi if [ $verbose -ge 3 ]; then printf "${MAGENTA}New RetensionDays: ${NO_COLOR}'%s'.\n" \ "${retension_days}" fi # get active retensiondate for given tape get_retensiondate $mediapool_name $volume_name if [ $? -eq 0 ]; then # calculate retensiondate in seconds date_iso_to_seconds "${volume_retensiondate}" if [ $? -eq 0 ]; then volume_retensiondate_seconds=$(($date_seconds + $retension_days * 24 * 60 * 60)) if [ $verbose -ge 3 ]; then printf "${MAGENTA}New RetensionDate in seconds is: ${NO_COLOR}'%s'.\n" \ "${volume_retensiondate_seconds}" fi # format to iso date_seconds_to_string "${volume_retensiondate_seconds}" if [ $? -eq 0 ]; then volume_retensiondate=${date_iso} if [ $verbose -ge 2 ]; then date_string_tz=$(echo $volume_retensiondate | cut -c 1-8) volume_date=$(date --date="${date_string_tz}Z") printf "${MAGENTA}RetensionDate is: ${NO_COLOR}'%s'.\n" \ "${volume_date}" fi fi fi fi # update retensionsdate if [ ${#volume_name} -ge 1 ]; then cmd="jq --monochrome-output --ascii-output '.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .Member \ | map(.VolumeName == \"$volume_name\") | index(true)' \ ${mediapools_json}" tape_index=$(eval $cmd) if [ $? -eq 0 ]; then cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .Member[$tape_index] \ | .RetensionDate) \ |= \"$volume_retensiondate\" ' \ ${mediapools_json} > $XDG_RUNTIME_DIR/$json_file" $(eval $cmd) if [ $? -eq 0 ]; then cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json" else return 1 fi else return 1 fi if [ $verbose -ge 3 ]; then printf "${MAGENTA}New RetensionDate in UTC for volume ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ "$volume_name" "$volume_retensiondate" fi fi } update_retensiondate () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1:-0} local volume_name=${2} local date_string=${3:-now} if [ $verbose -ge 1 ]; then printf "${BLUE}update_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 else json_path=${mediapools_json%/*} json_file=${mediapools_json##*/} cp "$mediapools_json" $XDG_RUNTIME_DIR/$json_file fi if [ ${#volume_name} -ge 1 ]; then # update retension date for given volume (date field in utc) if [ "${date_string}" = "now" ]; then volume_retensiondate=$($date_cmd) else volume_retensiondate=$date_string fi cmd="jq --monochrome-output --ascii-output '.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .Member \ | map(.VolumeName == \"$volume_name\") | index(true)' \ ${mediapools_json}" tape_index=$(eval $cmd) if [ $? -eq 0 ]; then cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \ | select(.Name == \"$mediapool_name\") \ | .Member[$tape_index] \ | .RetensionDate) \ |= \"$volume_retensiondate\" ' \ ${mediapools_json} > $XDG_RUNTIME_DIR/$json_file" $(eval $cmd) if [ $? -eq 0 ]; then cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json" else return 1 fi else return 1 fi if [ $verbose -ge 3 ]; then printf "${MAGENTA}RetensionDate for for volume ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ "$volume_name" "$volume_retensiondate" fi fi } usage () { cat < ) --color Enable colored output messages --get-lastwrite extract LastWrite attribute from given Pool member (JSON-File) (input attribute: []) without given volume_name, extract Pool member that was last witten to --get-mediapool-name extract MediaPool name for given VolumeName from configuration (JSON-File) (input attribute: ) --get-mediapools extract MediaPool names from configuration (JSON-File) --get-mediapolicy extract MediaPolicy attribute from Pool configuration (JSON-File) (input attribute: []) --get-poolmember extract VolumeName attribute from Pool configuration (JSON-File) (input attribute: []) --get-poolmember-next extract VolumeName for next usable Pool member (JSON-File) (input attribute: [volume_name]) --get-retensiondate extract RetensionDate attribute from Pool configuration (JSON-File) (input attribute: []) --get-retensiondays extract DefaultRetensionDays attribute from Pool configuration (JSON-File) (input attribute: ) --get-slot extract Slot attribute from Pool configuration (JSON-File) (input attribute: []) --ltfs-getattribute report ltfs extended attribute for mounted tape (input attribute: ) --ltfs-format format tape (input attribute: ) --ltfs-is-mounted returns true, if ltfs tape is already mounted --ltfs-mount mount a ltfs tape (returns true on success) --ltfs-reformat reformat mounted tape (input attribute: []) --ltfs-umount unmount a ltfs tape (returns true on success) --media-change Update RetensionDate for last written Pool-Tape and und exchange tapes in slots (input attribute: []) --mtx-exchange exchange tapes in slots (input attribute: ) --mtx-getlabel report tape label/barcode for tape in drive (input attribute: ) --mtx-inventory run inquriy task for tape-changer --mtx-load load a tape to target slot (input attribute: ) --mtx-status list changer slot status --mtx-transfer transfer a tape to target slot (input attribute: ) --mtx-unload unload a tape (input attribute: slot_source drive) --mount make tape accessible for OS (input attribute: []) -r, --remote 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. --update-lastwrite update LastWrite attribute for given TapeName in Pool (JSON-File) (input attribute: [ ) --update-retensiondate update RetensionDate attribute for given TapeName in Pool (JSON-File) (input attribute: [ ) --update-retensiondays update DefaultRetensionDays attribute for given Pool (JSON-File) (input attribute: ) --use-mtx use mtx loader handling. If not specified, all mtx commands will use default device ($default_changer_device) -v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3) --version show program version EOF --dry-run perform a trial run where no changes are made. 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 add-retensiondays) add_retensiondays_to_retensiondate "${mediapool_name}" "${volume_name}" "${retension_days}" if test $? -eq 0; then date_string_to_iso "${volume_retensiondate}" if [ $? -eq 0 ]; then volume_date=$(date --date="${date_iso}Z") printf "${MAGENTA}New RetensionDate date for ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}'%s'.\n" \ "${volume_name}" "${volume_date}" fi else if [ $verbose -ge 1 ]; then printf "${RED}Error: ${MAGENTA}No RetensionDate for ${GREEN}'%s'${MAGENTA} in media-pool ${GREEN}%s${NO_COLOR}\n" \ "${mediapool_name}" "${volume_name}" fi fi return $? ;; 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 return $? ;; get-mediapool-name) get_mediapool_name "${volume_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}MediaPool name: ${GREEN}'%s'${NO_COLOR}\n" \ "${mediapool_name}" fi fi return $? ;; get-mediapools) get_mediapool_names if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}MediaPool names: ${GREEN}'%s'${NO_COLOR}\n" \ "${mediapool_names}" fi fi return $? ;; 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 return $? ;; get-lastwrite) valid_member=0 get_lastwrite "${mediapool_name}" "${volume_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then date_string_to_iso ${volume_lastwrite} if [ $? -eq 0 ]; then volume_date=$(date --date="${date_iso}Z") printf "${MAGENTA}Last write date for Volume ${GREEN}'%s'${MAGENTA} from media-pool ${GREEN}'%s'${MAGENTA} is : ${NO_COLOR}'%s'.\n" \ "${volume_name}" "${mediapool_name}" "${volume_date}" fi fi else return 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 return $? ;; get-retensiondate) get_retensiondate "${mediapool_name}" "${volume_name}" if test $? -eq 0; then date_string_to_iso "${volume_retensiondate}" volume_date=$(date --date="${date_iso}Z") if [ $verbose -ge 1 ]; then printf "${MAGENTA}RetensionDate for ${GREEN}'%s'${MAGENTA} is:${NO_COLOR} %s.\n" \ "${volume_name}" "${volume_date}" fi else return 1 fi ;; get-mediapool-retensiondays) get_mediapool_retensiondays "${mediapool_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Default retension days for tapes in mediapool ${GREEN}'%s'${MAGENTA} is:${NO_COLOR} %s.\n" \ "${mediapool_name}" "${mediapool_defaultretensiondays}" fi else return 1 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 return $? ;; ltfs-format) ltfs_format "${volume_name}" "${tape_id}" ;; ltfs-getattribute) ltfs_is_mounted if test $? -eq 0; then ltfs_get_attribute "${ltfs_attribute_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}LTFS attribute ${GREEN}'%s'${MAGENTA} is: ${GREEN}'%s'${NO_COLOR}\n" \ "${ltfs_attribute_name}" "${ltfs_attribute_value}" fi fi fi return $? ;; ltfs-is-mounted) ltfs_is_mounted if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}LTFS Tape is-mounted: ${GREEN}true${NO_COLOR}\n" fi fi return $? ;; ltfs-mount) ltfs_mount if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}LTFS Tape mount: ${GREEN}true${NO_COLOR}\n" fi fi return $? ;; ltfs-reformat) ltfs_reformat "${volume_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}LTFS Tape reformat: ${GREEN}%s${NO_COLOR}\n" \ "${volume_name}" fi fi return $? ;; ltfs-umount) ltfs_umount if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}LTFS Tape unmount: ${GREEN}true${NO_COLOR}\n" fi fi return $? ;; media-change) media_change $mediapool_name $volume_name if [ $verbose -ge 1 ]; then if [ ${#volume_name} -ge 1 ]; then printf "${MAGENTA}Media changed in pool: ${GREEN}%s${NO_COLOR} (Volume-Name: ${GREEN}%s${NO_COLOR})\n" \ "${mediapool_name}" "${volume_name}" else printf "${MAGENTA}Media changed in pool: ${GREEN}%s${NO_COLOR}\n" \ "${mediapool_name}" fi fi return $? ;; mount) mount_tape $mediapool_name $volume_name return $? ;; mtx-exchange) mtx_exchange "${slot_source}" "${slot_target}" return $? ;; mtx-getlabel) mtx_getlabel $slot_source if [ $verbose -ge 1 ]; then printf "${MAGENTA}Tape Label: ${GREEN}%s${NO_COLOR}\n" \ "${volume_name_active}" fi return $? ;; mtx-inventory) mtx_inventory return $? ;; mtx-load) mtx_load "${slot_source}" "${drive}" return $? ;; mtx-transfer) mtx_transfer "${slot_source}" "${slot_target}" return $? ;; mtx-unload) mtx_unload "${slot_source}" "${drive}" return $? ;; mount) mount_tape "${mediapool_name}" "${volume_name}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}Tape ${GREEN}'%s'${MAGENTA} for ${GREEN}'%s'${MAGENTA} mounted${NO_COLOR}\n" \ "${volume_name}" "${mediapool_name}" fi fi return $? ;; update-lastwrite) update_lastwrite "${mediapool_name}" "${volume_name}" "${date_string}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then date_string_to_iso "${volume_lastwrite}" if [ $? -eq 0 ]; then volume_date=$(date --date="${date_iso}Z") printf "${MAGENTA}LastWrite date for ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}'%s'.\n" \ "${volume_name}" "${volume_date}" fi fi fi return $? ;; update-mediapool-retensiondays) update_mediapool_retensiondays "${mediapool_name}" "${mediapool_defaultretensiondays}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then printf "${MAGENTA}New DefaultRetensionDays attribute for ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}'%s'.\n" \ "${mediapool_name}" "${mediapool_defaultretensiondays}" fi fi return $? ;; update-retensiondate) update_retensiondate "${mediapool_name}" "${volume_name}" "${date_string}" if test $? -eq 0; then if [ $verbose -ge 1 ]; then date_string_to_iso "${volume_retensiondate}" if [ $? -eq 0 ]; then volume_date=$(date --date="${date_iso}Z") printf "${MAGENTA}RetensionDate date for ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}'%s'.\n" \ "${volume_name}" "${volume_date}" fi fi fi return $? ;; esac