diff --git a/Makefile b/Makefile index 9093dea..5337093 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,8 @@ 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)/ + @install -dm755 $(ETC_DIR)/dsnap-sync + @install -Dm644 etc/dsnap-sync/* -t $(ETC_DIR)/dsnap-sync/ + @install -dm755 $(ETC_DIR)/snapper/config-templates + @install -Dm644 etc/snapper/config-templates/* -t $(ETC_DIR)/snapper/config-templates/ + @install -Dm644 $(DSNAP_SYNC_EXAMPLES)/* -t $(DOC_DIR)/ diff --git a/bin/tape-admin b/bin/tape-admin index d4b1921..6058cc4 100755 --- a/bin/tape-admin +++ b/bin/tape-admin @@ -1,6 +1,6 @@ #! /bin/dash -# tape_admin +# tape-admin # https://github.com/rzerres/dsnap-sync # Copyright (C) 2017, 2018 Ralf Zerres @@ -22,27 +22,34 @@ # Helper routines for tape handling progname="${0##*/}" -version="0.0.1" +version="0.0.5" # global variables color=0 -date_cmd="date -u +%Y%M%d%H%M%S" +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="" -# -MTX=mtx +# helper programms +ATTR=attr JQ=jq LTFS=ltfs LTFSCK=ltfsck MKLTFS=mkltfs +MTX=mtx +SSH=ssh # ascii color BLUE= @@ -58,11 +65,16 @@ NO_COLOR= 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; } } @@ -91,6 +103,53 @@ compare_date () { 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_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 @@ -101,6 +160,64 @@ error () { 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 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} @@ -112,7 +229,7 @@ get_mediapolicy () { 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" \ + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}\n" \ "$mediapools_json" fi exit 1 @@ -125,6 +242,7 @@ get_mediapolicy () { | .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" \ @@ -132,6 +250,34 @@ get_mediapolicy () { 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_poolmember () { # return: 0-> volume_name match; 1 -> volume_name does not match local mediapools_json=${mediapools:-$mediapools_json} @@ -144,7 +290,7 @@ get_poolmember () { 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" \ + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}\n" \ "$mediapools_json" fi exit 1 @@ -156,6 +302,7 @@ get_poolmember () { | .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" \ @@ -163,9 +310,9 @@ get_poolmember () { fi for i in $poolmember ; do - if test "$i" = "\"${volume_name}\""; then + 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" \ + printf "${MAGENTA}volume_name ${GREEN}'%s'${MAGENTA} is member of media-pool ${GREEN}'%s'${NO_COLOR}\n" \ "$i" "$mediapool_name" fi return 0 @@ -178,7 +325,7 @@ get_poolmember () { get_poolmember_next () { local mediapools_json=${mediapools:-$mediapools_json} local mediapool_name=${1} - local volume_name=${2} + local volume_name=${2:-"none"} if [ $verbose -ge 1 ]; then printf "${BLUE}get_poolmember_next...${NO_COLOR}\n" @@ -186,7 +333,7 @@ get_poolmember_next () { 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" \ + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}\n" \ "$mediapools_json" fi exit 1 @@ -194,10 +341,14 @@ get_poolmember_next () { cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ - | .Member[] \ + | .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 @@ -211,8 +362,10 @@ get_poolmember_next () { break; fi fi - if [ $member = \"$volume_name\" ]; then - volume_index=$(($i+1)) + if [ ${#member} -ge 1 ]; then + if [ $member = $volume_name ]; then + volume_index=$(($i+1)) + fi fi i=$(($i + 1)) done @@ -234,7 +387,7 @@ get_slot () { 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" \ + printf "${RED}Error:${MAGENTA} media-pool file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}\n" \ "$mediapools_json" fi exit 1 @@ -266,38 +419,75 @@ get_retensiondate () { 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" \ + 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[] \ + cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \ | select(.Name == \"${mediapool_name}\") \ | .Member[] \ - | select(.VolumeName == \"${volume_name}\") \ + | 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" + if [ ${#volume_retensiondate} -gt 1 ]; then + volume_retensiondate=$(echo $volume_retensiondate | sed -e 's/"//g') + 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 + 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 tape_id=${2} + 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=${tape_id} 2>${ERRFILE} + ${MKLTFS} --device=$ltfs_devname --volume-name=${volume_name} --tape-serial=${volume_serial} 2>${ERRFILE} RET=$? + rm -f ${ERRFILE} + return $RET } ltfs_is_mounted () { @@ -329,24 +519,45 @@ ltfs_mount () { 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 + ${LTFS} -o devname=$ltfs_devname -o verbose=$verbose $ltfs_mountpoint 2>${ERRFILE} else - ${LTFS} -o devname=$ltfs_devname -o verbose=0 $ltfs_mountpoint + ${LTFS} -o devname=$ltfs_devname -o verbose=0 $ltfs_mountpoint 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_umount () { if [ $verbose -ge 1 ]; then - printf "${BLUE}ltfs_unmount...${NO_COLOR}\n" + printf "${BLUE}ltfs_umount...${NO_COLOR}\n" fi @@ -412,6 +623,7 @@ mtx_exchange () { make_err_file ${MTX} -f $changer_device exchange $slot_source $slot_target 2>${ERRFILE} RET=$? + rm -f ${ERRFILE} } mtx_getlabel () { @@ -433,9 +645,9 @@ mtx_getlabel () { printf "Calling: ${GREEN}%s${MAGENTA} -f ${GREEN}%s${MAGENTA} %s${NO_COLOR}\n" \ "${MTX}" "${changer_device}" "${slot_source}" fi - volume_name=$(perl -ne ' + volume_name_active=$(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} + volume_name_active=$(echo $volume_name_active | sed -e 's/ *$//g') ;; [0-9][0-8] | [0-9]) if [ $verbose -ge 2 ]; then @@ -492,6 +704,7 @@ mtx_load () { make_err_file ${MTX} -f $changer_device load $slot_source $drive 2>${ERRFILE} RET=$? + rm -f ${ERRFILE} } mtx_status () { @@ -517,6 +730,7 @@ mtx_transfer () { make_err_file ${MTX} -f $changer_device transfer $slot_source $slot_target 2>${ERRFILE} RET=$? + rm -f ${ERRFILE} } mtx_unload () { @@ -545,13 +759,11 @@ mtx_unload () { 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" \ + printf "${RED}Fail: %s -f %s unload slot=%s drive=%s${NO_COLOR}\n" \ "${MTX}" "${changer_device}" "${slot_source}" "${drive}" fi fi - exit $RET - - + return $RET } parse_params () { @@ -569,7 +781,7 @@ parse_params () { dryrun=1 shift 1 ;; - --get-retensiondate) + --get-lastwrite) shift 1 pool_params=${*} pool_params="${pool_params%% -*}" @@ -580,7 +792,11 @@ parse_params () { test $count -ge 2 && volume_name="$2" set -- $params shift $count - cmd=get-retensiondate + cmd=get-lastwrite + ;; + --get-mediapools) + shift 1 + cmd=get-mediapools ;; --get-poolmember) shift 1 @@ -608,6 +824,19 @@ parse_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-slot) shift 1 pool_params=${*} @@ -621,19 +850,19 @@ parse_params () { shift $count cmd=get-slot ;; - --mount-tape) + --mount) 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 -lt 1 && usage + mediapool_name="$1" test $count -ge 2 && volume_name="$2" set -- $params shift $count - cmd=mount-tape + cmd=mount ;; -p|--port) port=$2 @@ -651,6 +880,17 @@ parse_params () { 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 + ;; --ltfs-format) shift 1 ltfs_params=${*} @@ -753,6 +993,36 @@ parse_params () { shift $count cmd=mtx-unload ;; + --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 + ;; -v|--verbose) verbose=$(($verbose + 1)) shift 1 @@ -848,64 +1118,219 @@ mount_tape () { ltfs_is_mounted if test $? -eq 0; then if [ $verbose -ge 1 ]; then - printf "${MAGENTA}LTFS Tape is-mounted: ${GREEN}%d${NO_COLOR}\n" \ - "$?" + printf "${MAGENTA}LTFS Tape is mounted.${NO_COLOR}\n" + fi + + # check that mounted tape is member of selected mediapool + mtx_getlabel 0 + if [ $? -eq 0 ]; then + get_poolmember ${mediapool_name} ${volume_name_active} + if [ $? -eq 1 ]; then + # unmount and recurse execution + 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 + # 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 - mtx_getlabel 0 + slot_source="0" + mtx_getlabel $slot_source if [ $verbose -ge 1 ]; then printf "${MAGENTA}Tape Label: ${GREEN}%s${NO_COLOR}\n" \ - "${volume_name}" + "${volume_name_active}" + fi + if [ "${#volume_name_active}" -ge 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then + 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 # check if given tape is poolmember of selected pool get_poolmember ${mediapool_name} ${volume_name} if [ $? -eq 0 ]; then - # volume_name match + # volume_name is member of given mediapool 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 + 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 does not match + # volume_name is not member of given pool ltfs_umount - mtx_unload 0 - # TODO - get_poolmember - get_poolmember_next ${mediapool_name} ${volume_name} - #mtx load ${volume_name_next} + 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 - get_slot ${mediapool_name} ${volume_name} - mtx_load ${volume_slot} - ltfs_mount - return 0 + if [ "${#volume_name}" -eq 0 ]; then + # set volume_name and last write date + get_lastwrite ${mediapool_name} + fi + # check label from tape in given drive slot + slot_source="0" + mtx_getlabel $slot_source + if [ $? -eq 0 ] && [ "${#volume_name_active}" -gt 1 ]; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}Tape Label: ${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 + if [ $? -eq 2 ]; then + # retensiondate has exposed: use it + 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 } @@ -924,22 +1349,183 @@ trapkill () { 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 for for volume ${GREEN}'%s'${MAGENTA} is: ${NO_COLOR}%s${NO_COLOR}\n" \ + "$volume_name" "$volume_lastwrite" + 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 < 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 + --color Enable colored output messages + --get-lastwrite extract LastWrite attribute from given Pool member (JSON-File) + (input attribute: [volume_name]) + without given volume_name, extract Pool member that was last witten to + --get-mediapools extract MediaPool names Pool configuration (JSON-File) + --get-mediapolicy extract MediaPolicy attribute from Pool configuration (JSON-File) + (input attribute: [volume_name]) + --get-poolmember extract VolumeName attribute from Pool configuration (JSON-File) + (input attribute: [volume_name]) + --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: [volume_name]) + --get-slot extract Slot attribute from Pool configuration (JSON-File) + (input attribute: [volume_name]) + --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-umount unmount a ltfs tape (returns true on success) + --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: []) + --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: [ ) + --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 + #-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. + # --dry-run perform a trial run where no changes are made. exit 0 } @@ -967,6 +1553,17 @@ case $cmd in "${volume_name}" "${volume_mediapolicy}" fi fi + return $? + ;; + get-mediapools) + get_mediapool_names + if test $? -eq 0; then + if [ $verbose -ge 1 ]; then + printf "${MAGENTA}MediaPool names:${NO_COLOR} %s.\n" \ + "${mediapool_names}" + fi + fi + return $? ;; get-poolmember) valid_member=0 @@ -978,16 +1575,34 @@ case $cmd in 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" \ + 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}" @@ -996,6 +1611,8 @@ case $cmd in printf "${MAGENTA}RetensionDate for ${GREEN}'%s'${MAGENTA} is:${NO_COLOR} %s.\n" \ "${volume_name}" "${volume_retensiondate}" fi + else + return 1 fi ;; get-slot) @@ -1006,71 +1623,115 @@ case $cmd in "${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}%d${NO_COLOR}\n" \ - "$?" + 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}%d${NO_COLOR}\n" \ - "$?" + printf "${MAGENTA}LTFS Tape mount: ${GREEN}true${NO_COLOR}\n" fi fi + return $? ;; ltfs-umount) ltfs_umount if test $? -eq 0; then if [ $verbose -ge 1 ]; then - printf "${MAGENTA}LTFS Tape unmount: ${GREEN}%d${NO_COLOR}\n" \ - "$?" + printf "${MAGENTA}LTFS Tape unmount: ${GREEN}true${NO_COLOR}\n" fi fi + 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}" + "${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-tape) + mount) 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}" "$?" + printf "${MAGENTA}Mount tape ${GREEN}'%s'${MAGENTA} for ${GREEN}'%s'${MAGENTA}: ${NO_COLOR}true.\n" \ + "${volume_name_active}" "${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-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 - -# 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 index 6fe9657..ce59dfd 100644 --- a/etc/dsnap-sync/MediaPools.json +++ b/etc/dsnap-sync/MediaPools.json @@ -1,32 +1,38 @@ -{ - "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" } - ] - } - ] +{ + "version": "0.0.4", + "_comment": "MediaPolicy: append, overwrite; Date values in UTC", + "MediaPools": "Media-Pools", + "MediaPool": [ + { + "Name": "Pool-1", + "DefaultRetensionDays": "0", + "Tape-Type": "LTO7", + "_comment": "MediaPool append", + "Member": [ + { "VolumeName": "Tape-0001", "VolumeId": "000001", "Slot": "1", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0002", "VolumeId": "000002", "Slot": "2", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0003", "VolumeId": "000003", "Slot": "3", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0004", "VolumeId": "000004", "Slot": "4", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0005", "VolumeId": "000005", "Slot": "5", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0006", "VolumeId": "000006", "Slot": "6", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0007", "VolumeId": "000007", "Slot": "7", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0008", "VolumeId": "000008", "Slot": "8", "MediaPolicy": "append", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" } + ] + }, + { + "Name": "Pool-2", + "DefaultRetensionDays": "7", + "Tape-Type": "LTO7", + "_comment": "MediaPool overwrite", + "Member": [ + { "VolumeName": "Tape-0009", "VolumeId": "000009", "Slot": "9", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0010", "VolumeId": "000010", "Slot": "10", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0011", "VolumeId": "000011", "Slot": "11", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0012", "VolumeId": "000012", "Slot": "12", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0013", "VolumeId": "000013", "Slot": "13", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0014", "VolumeId": "000014", "Slot": "14", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" }, + { "VolumeName": "Tape-0015", "VolumeId": "000015", "Slot": "15", "MediaPolicy": "overwrite", "RetensionDate": "20180824000000", "LastWrite": "20180824000000" } + ] + } + ] }