|
|
| Line 29: |
Line 29: |
|
| |
|
| =Configure On CentOS 6.5= | | =Configure On CentOS 6.5= |
| | Since I use disks I start with installing vchanger |
| | *http://wiki.bacula.org/doku.php?id=removable_disk |
| | The link above has a script but it looks like the one on sourceforge (which is referenced in the link) is newer and compiles. Even then, I decided to pull the rpm from pbone |
| | wget ftp5.gwdg.de/pub/opensuse/repositories/home:/pstorz/CentOS_CentOS-6/x86_64/vchanger-0.8.6-10.3.x86_64.rpm |
| | yum install vchanger-0.8.6-10.3.x86_64.rpm |
|
| |
|
| nano /etc/bareos/vchanger
| | ==Links== |
| | | *http://rpm.pbone.net/index.php3/stat/4/idpl/23671919/dir/centos_6/com/vchanger-0.8.6-10.3.i686.rpm.html |
| <pre>
| | *http://wiki.bacula.org/doku.php?id=removable_disk#vchanger |
| | | *http://superuser.com/questions/573816/how-do-i-configure-yum-to-use-additional-repositories |
| | |
| #!/bin/sh
| |
| #
| |
| # Bacula interface to virtual autochanger using removable disk drives
| |
| #
| |
| # Based (somewhat) on the "disk-changer" script from bacula 1.39.26
| |
| #
| |
| # Vchanger is a Bacula autochanger script that emulates a conventional
| |
| # magazine-based tape library device using removable disk drives.
| |
| # Partitions on the removable drives are used as virtual magazines,
| |
| # where each "magazine" contains the same number of virtual slots. Each
| |
| # "slot" holds one virtual tape, where a "tape" is a regular file that
| |
| # Bacula treats as a "Device Type = File" volume.
| |
| #
| |
| # This script will be invoked by Bacula using the Bacula Autochanger
| |
| # Interface and will be passed the following arguments:
| |
| #
| |
| # vchanger "changer-device" "command" "slot" "archive-device" "drive-index"
| |
| # $1 $2 $3 #4 #5
| |
| #
| |
| # See the Bacula documentation and the Bacula Removable Disk Howto for
| |
| # further details.
| |
| #
| |
| # Copyright (C) 2006 Josh Fisher
| |
| #
| |
| # Permission to use, copy, modify, distribute, and sell this software
| |
| # and its documentation for any purpose is hereby granted without fee,
| |
| # provided that the above copyright notice appear in all copies. This
| |
| # software is provided "as is" without express or implied warranty.
| |
| #
| |
| # This software 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.
| |
| #
| |
| # $Id: vchanger,v 0.7.4 2006/12/01 09:29:04 jfisher Exp $
| |
| | |
| #
| |
| # log whats done
| |
| #
| |
| # to turn on logging, uncomment the following line
| |
| #touch $wd/vchanger.log
| |
| #
| |
| | |
| #
| |
| # Write to a log file
| |
| # To log debugging info, create file /var/bacula/vchanger.log
| |
| # with write permission for bacula-sd user. To stop logging,
| |
| # delete file /var/bacula/vchanger.log
| |
| #
| |
| dbgfile="/var/bacula/vchanger.log"
| |
| function debug()
| |
| {
| |
| if test -e $dbgfile; then
| |
| echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
| |
| fi
| |
| }
| |
| | |
| #
| |
| # Return length of string $1
| |
| #
| |
| if [ `uname` = "FreeBSD" ]
| |
| then
| |
| function strlen ()
| |
| {
| |
| expr -- "$1" : ".*"
| |
| }
| |
| else
| |
| function strlen ()
| |
| {
| |
| expr length $1
| |
| }
| |
| fi
| |
| | |
| | |
| #
| |
| # Prepend zeros to $1 and return a string that is $2 characters long
| |
| #
| |
| function mklen ()
| |
| {
| |
| o1=$1
| |
| while [ `eval strlen ${o1}` -lt ${2} ]; do
| |
| o1="0${o1}"
| |
| done
| |
| echo $o1
| |
| }
| |
| | |
| #
| |
| # Get uid of $1 (or current user if $1 empty)
| |
| #
| |
| function get_uid() {
| |
| id $1 2>/dev/null | cut -d ' ' -f 1 | sed "s/uid=//" | cut -d '(' -f 1
| |
| }
| |
| | |
| | |
| #
| |
| # Initialize autochanger's state directory if not already initialized
| |
| #
| |
| function init_statedir() {
| |
| debug "Initializing $statedir"
| |
| # Create state directory if needed
| |
| if [ ! -d "${statedir}" ]; then
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "mkdir ${statedir} &>/dev/null" $su_uid
| |
| else
| |
| mkdir ${statedir} &>/dev/null
| |
| fi
| |
| if [ $? -ne 0 ]; then
| |
| echo "Could not create ${statedir}"
| |
| exit 1
| |
| fi
| |
| chmod 770 ${statedir} &>/dev/null
| |
| if [ $? -ne 0 ]; then
| |
| echo "Could not chmod ${statedir}"
| |
| exit 1
| |
| fi
| |
| fi
| |
| # Create nextmag file to hold max magazine index used
| |
| if [ ! -f "${statedir}/nextmag" ]; then
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "echo 0 >${statedir}/nextmag" $su_uid
| |
| else
| |
| echo 0 >${statedir}/nextmag
| |
| fi
| |
| if [ $? -ne 0 ]; then
| |
| echo "Could not create ${statedir}/nextmag"
| |
| exit 1
| |
| fi
| |
| chmod 660 ${statedir}/nextmag
| |
| if [ $? -ne 0 ]; then
| |
| echo "Could not chmod ${statedir}/nextmag"
| |
| exit 1
| |
| fi
| |
| fi
| |
| # Check nextmag value
| |
| nextmag=`cat "${statedir}/nextmag"`
| |
| if [ $? -ne 0 -o "${nextmag}" == "" -o $nextmag -lt 0 -o $nextmag -gt 99 ]; then
| |
| echo "${statedir}/nextmag has invalid value"
| |
| return 1
| |
| fi
| |
| # Create 'loaded' files for each virtual drive that hold the slot
| |
| # number currently loaded in that 'drive'
| |
| i=0
| |
| while [ $i -le $maxdrive ]; do
| |
| if [ ! -f "${statedir}/loaded${i}" ]; then
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "echo 0 >${statedir}/loaded${i}" $su_uid
| |
| else
| |
| echo 0 >${statedir}/loaded${i}
| |
| fi
| |
| if [ $? -ne 0 ]; then
| |
| echo "Could not create ${statedir}/loaded${i}"
| |
| exit 1
| |
| fi
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "chmod 660 ${statedir}/loaded${i}" $su_uid
| |
| else
| |
| chmod 660 ${statedir}/loaded${i}
| |
| fi
| |
| if [ $? -ne 0 ]; then
| |
| echo "Could not chmod ${statedir}/loaded${i}"
| |
| exit 1
| |
| fi
| |
| fi
| |
| i=`expr ${i} + 1`
| |
| done
| |
| }
| |
| | |
| | |
| #
| |
| # Get magazine index of currently loaded magazine
| |
| #
| |
| function get_magazine() {
| |
| debug "Get magazine index"
| |
| # Check for mountpoint dir
| |
| if [ ! -d ${mountpoint} ]; then
| |
| echo "No magazine loaded at ${mountpoint}"
| |
| exit 1
| |
| fi
| |
| # Check magazine for existing index
| |
| if [ ! -f "${mountpoint}/index" ]; then
| |
| echo "00"
| |
| return 1
| |
| fi
| |
| mi=`cat "${mountpoint}/index"`
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to read ${mountpoint}/index"
| |
| exit 1
| |
| fi
| |
| # must be 1-99
| |
| if [ $mi -lt 1 -o $mi -gt 99 ]; then
| |
| echo "Magazine has invalid index ${mi}"
| |
| exit 1
| |
| fi
| |
| # make magazine index 2 digits
| |
| eval mklen ${mi} 2
| |
| return 0
| |
| }
| |
| | |
| #
| |
| # Initialize magazine if not already initialized
| |
| #
| |
| function init_magazine() {
| |
| debug "Initializing magazine"
| |
| # Get max magazine index that has been used
| |
| nextmag=`cat "${statedir}/nextmag"`
| |
| if [ $? -ne 0 -o "${nextmag}" == "" ]; then
| |
| echo "Failed to read ${statedir}/nextmag"
| |
| exit 1
| |
| fi
| |
| # Check magazine for existing index
| |
| if [ -f "${mountpoint}/index" ]; then
| |
| # retrieve existing magazine index
| |
| mi=`cat "${mountpoint}/index"`
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to read ${mountpoint}/index"
| |
| exit 1
| |
| fi
| |
| # must be 1-99
| |
| if [ $mi -lt 1 -o $mi -gt 99 ]; then
| |
| echo "Magazine has invalid index ${mi}"
| |
| exit 1
| |
| fi
| |
| else
| |
| # new magazine, so assign it the next avail index
| |
| mi=`expr ${nextmag} + 1`
| |
| if [ $mi -lt 0 -o $mi -gt 99 ]; then
| |
| echo "Max magazines exceeded"
| |
| exit 1
| |
| fi
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "echo ${mi} >${mountpoint}/index" $su_uid
| |
| else
| |
| echo ${mi} >${mountpoint}/index
| |
| fi
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to write ${mountpoint}/index"
| |
| exit 1
| |
| fi
| |
| chmod 640 ${mountpoint}/index 2>/dev/null
| |
| fi
| |
| # make sure max index used is up to date
| |
| if [ $mi -gt $nextmag ]; then
| |
| echo $mi 2>/dev/null >"${statedir}/nextmag"
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to update ${statedir}/nextmag"
| |
| exit 1
| |
| fi
| |
| fi
| |
| # make magazine index 2 digits
| |
| magindex=`eval mklen ${mi} 2`
| |
| # setup slot files (ie. virtual tapes)
| |
| i=1
| |
| while [ $i -le $magslots ]; do
| |
| s=`eval mklen ${i} 3`
| |
| f="${mountpoint}/m${magindex}s${s}"
| |
| if [ ! -f "${f}" ]; then
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "touch ${f} &>/dev/null" $su_uid
| |
| else
| |
| touch ${f} &>/dev/null
| |
| fi
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to create ${f}"
| |
| exit 1
| |
| fi
| |
| fi
| |
| i=`expr ${i} + 1`
| |
| done
| |
| return 0
| |
| }
| |
| | |
| | |
| #
| |
| # Get sd status with bconsole
| |
| #
| |
| function bconsole_sd_status() {
| |
| debug "Doing 'status storage' with bconsole"
| |
| $bconsole <<EOD_SDSTAT
| |
| status storage=$baculasd
| |
| quit
| |
| EOD_SDSTAT
| |
| }
| |
| | |
| | |
| #
| |
| # Delete volume with bconsole param1 = slot
| |
| #
| |
| function bconsole_delete_volume() {
| |
| debug "Doing 'delete volume' with bconsole"
| |
| s=`eval mklen $1 3`
| |
| $bconsole <<EOD_DELVOL
| |
| delete volume=m${magindex}s${s}
| |
| yes
| |
| quit
| |
| EOD_DELVOL
| |
| }
| |
| | |
| | |
| #
| |
| # Label volumes from barcodes with bconsole
| |
| #
| |
| function bconsole_label_barcodes() {
| |
| debug "Doing 'label barcodes' with bconsole"
| |
| $bconsole <<EOD_SDSTAT
| |
| label storage=$baculasd pool=$purgepool drive=0 barcodes
| |
| yes
| |
| quit
| |
| EOD_SDSTAT
| |
| }
| |
| | |
| #
| |
| # Checks if any of magazine's volumes are in use
| |
| #
| |
| function volumes_in_use() {
| |
| debug "Checking for in use volumes"
| |
| inuse=""
| |
| bconsole_sd_status | while read f; do
| |
| #echo $f
| |
| a=`echo ${f} | grep ^${magindex}`
| |
| if [ "$a" != "" -a "$inuse" == "" ]; then
| |
| inuse=`echo $f | cut -d ' ' -f 1`
| |
| fi
| |
| done
| |
| if [ "$inuse" != "" ]; then
| |
| echo $inuse
| |
| fi
| |
| }
| |
| | |
| | |
| #
| |
| # Unload all drives
| |
| #
| |
| function unload_drives() {
| |
| debug "Unloading all drives"
| |
| vuse=`eval volumes_in_use`
| |
| if [ "${vuse}" != "" ]; then
| |
| return 1
| |
| fi
| |
| i=0
| |
| while [ $i -le $maxdrive ]; do
| |
| unlink "${statedir}/drive${i}" 2>/dev/null >/dev/null
| |
| echo "0" >"${statedir}/loaded${i}"
| |
| i=`expr ${i} + 1`
| |
| done
| |
| return 0
| |
| }
| |
| | |
| | |
| #
| |
| # check parameter count on commandline
| |
| #
| |
| function check_parm_count() {
| |
| pCount=$1
| |
| pCountNeed=$2
| |
| if test $pCount -lt $pCountNeed; then
| |
| echo "usage: vchanger ctl-device command [slot archive-device drive-index]"
| |
| echo " Insufficient number of arguments arguments given."
| |
| if test $pCount -lt 2; then
| |
| echo " Mimimum usage is first two arguments ..."
| |
| else
| |
| echo " Command expected $pCountNeed arguments"
| |
| fi
| |
| exit 1
| |
| fi
| |
| }
| |
| | |
| # Setup arguments
| |
| check_parm_count $# 2
| |
| ctl=$1
| |
| cmd=$2
| |
| slot=$3
| |
| device=$4
| |
| drive=$5
| |
| | |
| # Setup default config values
| |
| baculasd=
| |
| baculasd_user=bacula
| |
| bconsole="/etc/bacula/bconsole"
| |
| magslots=10
| |
| maxdrive=0
| |
| mountpoint=
| |
| purgepool="Scratch"
| |
| statedir=
| |
| | |
| # Pull in conf file
| |
| if [ -f $ctl ]; then
| |
| . $ctl
| |
| else
| |
| echo "Config file ${ctl} not found"
| |
| exit 1
| |
| fi
| |
| | |
| # When invoked by root, create files/dirs as bacula-sd user
| |
| su_uid=
| |
| myuid=`eval get_uid`
| |
| if [ "$myuid" == "0" -a "$baculasd_user" != "" ]; then
| |
| buid=`eval get_uid $baculasd_user`
| |
| if [ "$buid" == "" ]; then
| |
| echo "bacula-sd user $baculasd_user not found"
| |
| exit 1
| |
| fi
| |
| if [ "$buid" != "0" ]; then
| |
| su_uid=baculasd_user
| |
| fi
| |
| fi
| |
| | |
| # check for required config values | |
| if [ "${mountpoint}" == "" ]; then
| |
| echo "Required variable 'mountpoint' not defined in ${ctl}"
| |
| exit 1
| |
| fi
| |
| if [ "${baculasd}" == "" ]; then
| |
| echo "Required variable 'baculasd' not defined in ${ctl}"
| |
| exit 1
| |
| fi
| |
| if [ "${statedir}" == "" ]; then
| |
| echo "Required variable 'statedir' not defined in ${ctl}"
| |
| exit 1
| |
| fi
| |
| if [ "${magslots}" == "" -o $magslots -lt 1 -o $magslots -gt 999 ]; then
| |
| echo "Ivalid value for 'magslots' in ${ctl}"
| |
| exit 1
| |
| fi
| |
| if [ "${maxdrive}" == "" -o $maxdrive -lt 0 -o $maxdrive -ge $magslots ]; then
| |
| echo "Invalid value for 'maxdrive' in ${ctl}"
| |
| exit 1
| |
| fi
| |
| if [ "${bconsole}" == "" -o ! -f "${bconsole}" ]; then
| |
| echo "Ivalid value for 'bconsole' in ${ctl}"
| |
| exit 1
| |
| fi
| |
| if [ "${purgepool}" == "" ]; then
| |
| echo "Invalid value for 'purgepool' in ${ctl}"
| |
| exit 1
| |
| fi
| |
| | |
| # attempt to set defaults for params not specified on command line
| |
| if [ "${drive}" == "" ]; then
| |
| drive=0
| |
| fi
| |
| if [ "${device}" == "" ]; then
| |
| device="${statedir}/drive${drive}"
| |
| fi
| |
| | |
| # make sure archive device makes sense
| |
| if [ "${device}" != "${statedir}/drive${drive}" ]; then
| |
| echo "Param 4 (archive device) must be ${statedir}/driveN,"
| |
| echo " where N is the drive number passed as param 5"
| |
| exit 1
| |
| fi
| |
| | |
| # Initialize state directory for this autochanger
| |
| init_statedir
| |
| | |
| debug "Parms: $ctl $cmd $slot $device $drive"
| |
| | |
| #
| |
| # Process command
| |
| #
| |
| case $cmd in
| |
| list)
| |
| check_parm_count $# 2
| |
| debug "Doing list command"
| |
| init_magazine
| |
| if [ $? -ne 0 ]; then
| |
| echo "Magazine Not Loaded"
| |
| exit 1
| |
| fi
| |
| i=1
| |
| while [ $i -le $magslots ]; do
| |
| s=`eval mklen ${i} 3`
| |
| echo "${i}:m${magindex}s${s}"
| |
| i=`expr ${i} + 1`
| |
| done
| |
| ;;
| |
| slots)
| |
| check_parm_count $# 2
| |
| debug "Doing slots command"
| |
| echo $magslots
| |
| ;;
| |
| load)
| |
| check_parm_count $# 5
| |
| debug "Doing load slot $slot into drive $drive"
| |
| if [ $drive -gt $maxdrive ]; then
| |
| echo "Drive ($drive) out of range (0-${maxdrive})"
| |
| exit 1
| |
| fi
| |
| if [ $slot -gt $magslots ]; then
| |
| echo "Slot ($slot) out of range (1-$magslots)"
| |
| exit 1
| |
| fi
| |
| ld=`cat "${statedir}/loaded${drive}"`
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to read ${statedir}/loaded${drive}"
| |
| exit 1
| |
| fi
| |
| if [ $ld -eq 0 ]; then
| |
| unlink "${device}" &>/dev/null
| |
| # make sure slot is not loaded in another drive
| |
| i=0
| |
| while [ $i -le $maxdrive ]; do
| |
| if [ $i -ne $drive ]; then
| |
| ldi=`cat "${statedir}/loaded${i}"`
| |
| if [ $ldi -eq $slot ]; then
| |
| echo "Storage Element ${slot} Empty (loaded in drive ${i})"
| |
| exit 1
| |
| fi
| |
| fi
| |
| i=`expr ${i} + 1`
| |
| done
| |
| init_magazine
| |
| if [ $? -ne 0 ]; then
| |
| echo "Magazine Not Loaded"
| |
| exit 1
| |
| fi
| |
| s=`eval mklen ${slot} 3`
| |
| if [ "$su_uid" != "" ]; then
| |
| su -c "ln -s '${mountpoint}/m${magindex}s${s}' '${device}'" $su_uid
| |
| else
| |
| ln -s "${mountpoint}/m${magindex}s${s}" "${device}"
| |
| fi
| |
| echo $slot >"${statedir}/loaded${drive}"
| |
| else
| |
| echo "Drive ${drive} Full (Storage element ${ld} loaded)"
| |
| exit 1
| |
| fi
| |
| ;;
| |
| unload)
| |
| check_parm_count $# 5
| |
| debug "Doing unload drive $drive into slot $slot"
| |
| if [ $drive -gt $maxdrive ]; then
| |
| echo "Drive ($drive) out of range (0-${maxdrive})"
| |
| exit 1
| |
| fi
| |
| if [ $slot -gt $magslots ]; then
| |
| echo "Slot ($slot) out of range (1-$magslots)"
| |
| exit 1
| |
| fi
| |
| ld=`cat "${statedir}/loaded${drive}"`
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to read ${statedir}/loaded${drive}"
| |
| exit 1
| |
| fi
| |
| if [ $slot -eq $ld ]; then
| |
| echo "0" >"${statedir}/loaded${drive}"
| |
| if [ $? -ne 0 ]; then
| |
| echo "Failed to write ${statedir}/loaded${drive}"
| |
| exit 1
| |
| fi
| |
| unlink "${device}" 2>/dev/null >/dev/null
| |
| exit 0
| |
| fi
| |
| if [ $ld -eq 0 ]; then
| |
| echo "Drive ${drive} Is Empty"
| |
| exit 1
| |
| else
| |
| echo "Storage Element ${slot} is Already Full"
| |
| exit 1
| |
| fi
| |
| ;;
| |
| loaded)
| |
| check_parm_count $# 5
| |
| debug "Doing loaded command for drive $drive"
| |
| if [ $drive -gt $maxdrive ]; then
| |
| echo "Drive ($drive) out of range (0-${maxdrive})"
| |
| exit 1
| |
| fi
| |
| if [ $slot -gt $magslots ]; then
| |
| echo "Slot ($slot) out of range (1-$magslots)"
| |
| exit 1
| |
| fi
| |
| cat "${statedir}/loaded${drive}"
| |
| ;;
| |
| purge)
| |
| check_parm_count $# 2
| |
| debug "Doing purge command"
| |
| magindex=`eval get_magazine`
| |
| if [ "$magazine" == "00" ]; then
| |
| echo No magazine loaded
| |
| fi
| |
| cm=""
| |
| while [ "$cm" != "y" -a "$cm" != "n" ]; do
| |
| echo -n "Purge all volumes on magazine $magindex (y/n)? "
| |
| read cm
| |
| if [ "$cm" == "Y" ]; then
| |
| cm="y"
| |
| fi
| |
| if [ "$cm" == "N" ]; then
| |
| cm="n"
| |
| fi
| |
| done
| |
| if [ "$cm" == "n" ]; then
| |
| exit 0
| |
| fi
| |
| echo "Unloading $baculasd drives"
| |
| unload_drives
| |
| if [ $? -ne 0 ]; then
| |
| echo Volume `eval volumes_in_use` in use...aborting
| |
| exit 1
| |
| fi
| |
| echo "Deleting magazine $magindex volumes"
| |
| i=1
| |
| while [ $i -le $magslots ]; do
| |
| bconsole_delete_volume $i &>/tmp/vclog
| |
| rm -f "${mountpoint}/m${magindex}s${s}"
| |
| echo "deleted volume m${magindex}s${s}"
| |
| i=`expr ${i} + 1`
| |
| done
| |
| echo "Doing 'label barcodes' to re-create magazine's volumes"
| |
| bconsole_label_barcodes | while read f; do
| |
| echo $f
| |
| done
| |
| ;;
| |
| *)
| |
| echo "Command not recognized"
| |
| exit 1
| |
| ;;
| |
| esac
| |
| | |
| exit 0
| |
| # eof
| |
| </pre>
| |