Amanda on Solaris 8 with Spectra Logic Treefrog(2000). Hardware: Sun Enterprise 250, Spectra Logic Treefrog with AIT-1 tape(SCSI interface). Software: Solaris 8, amanda 2.4.3b2, tar 1.13.19, mtx 1.2.16rel
This document shows how I got Treefrog to work under solaris 8. Big thank to Stephen Carville on Amanda-user mailing list. He wrote a terrific changer glue script for Treefrog in perl. Without his help, it would probably take another light year to get my own backup working. Disclaimer: This document is solely based on my own experience. I do NOT guarantee the methods will work in your environment. Attachment description: chg-spectra.pl - Stephen Carville's perl changer glue script changer.conf - changer's configuration file for chg-2.pl amanda.conf - sample configuration file given to me by Stephen Carville barlabler.pl - perl script I wrote to label tape according to its barcode. Here it goes: 1. Install your treefrog. Remeber to set the configuration dial in the back of the Treefrog to "9" so it works properly under Solaris. Read your Treefrog manual on how to change the dial. You might need to modify /dev/kernel/sgen.conf to have changer(robotic arm) working properly. The tape drive should work without any modification. Remeber to reboot after sgen.conf modification. If install correctly, your changer device descriptor file would be in /dev/scsi/changer, and device would be in /dev/rmt (See attachment) 2. Install MTX. It's a program that controls SCSI media changer devices. You can download it from sourceforge. Confirm that you can use 'mtx' to control your changer and 'mt' to control your tape device. Read the man page on how to use those two commands. 3. Configure and install amanda. Follow docs/INSTALL, and you should have no problem setting it up. Remember to copy chg-spectra.pl to Amanda's changer glue script directory, default is in /usr/local/libexec. 4. Set up your backup configuration files by copying the default amanda.conf.chg-scsi file from /usr/local/etc/amanda to /usr/local/etc/amanda/<conf Name>/amanda.conf. Change chg-scsi script reference in amanda.conf to attached changer glue script(chg-spectra, see attached sample amanda.conf) and modify tapedev and changerdev to point to correct device files. Make sure you point the log, index, and debug directories to appropriate place. Especially debug files, they're very useful to debug amanda. 5. Amanda needs to label your tapes before it can write to them. If you would like the labels to match your barcode, use the attached perl script that I wrote(barlabeler). Make sure 'amlabel' and 'mtx' are in your path, they're in "/usr/local/sbin" by default. I like to label them with barcode because it's easier for me to locate tapes when I need them to restore. I wont need a separate label for tapes. If you do not want to match labels with the barcodes, you can label the tapes by manually issue amlabel command to each tape. 6. Run 'amcheck <conf name>' and correct every errors it reports back. Amanda doesn't create some directories and files by itself, so you'll have to do it yourself. Stuff like log directory, index directory, gnu-tar directory, or /etc/amandates file. You also need to make sure permission on those directories and files are set correctly. Debug files are very useful on figuring out where went wrong, so please consult them first when you run into problem. 7. When 'amcheck' does not return any more error, you can run some test dump. Once you make sure you can run dump and restore correctly, you can delete log directory's content, index directory's content(if you setup index), and the file "tapelist", then amanda will run fresh from scratch again.
#!/usr/local/bin/perl # Mark Lin. 2/25/2002 # Works with MTX version 1.2.16rel under solaris. # This little perl script label every tape same as their barcode. # It works by using mtx staus to get the barcode and use amlabel # to label each tape with barcode. use strict; my (@result, @test, $line, $line2, $stat); if (!@ARGV) { print "Usage: barlabler <conf>\n"; exit 1; } @result = `mtx status 2>&1`; foreach $line (@result) { if ($line =~ m/Data Transfer Element 0:Full \(Storage Element (\d+).*([EC]\d{5})/) { # preserve the slot number and barcode #print "$1, $2\n"; @test = `amlabel @ARGV[0] $2`; if ($_) { print "Label #2 Failed. reason below:\n"; foreach $line2 (@test) { print "$line2"; } } else { print "label $2 in drive $1 succeed\n"; } next; } # Change your regular expression if your tapes has different barcode. if ($line =~ m/\s+Storage Element (\d+).*=([EC]\d{5})/) { #print "slot: $1 volume: $2\n"; @test = `amlabel @ARGV[0] $2 slot $1`; if ($_) { print "Label #2 Failed. reason below:\n"; foreach $line2 (@test) { print "$line2"; } } else { print "label $2 in slot $1 succeed\n"; } next; } }
changer.conf
Description: Binary data
amanda.conf
Description: Binary data
#!/usr/bin/perl -w # # Tape changer glue script for a Spectra 2000 tape changer # Does not include the 'clean' option # # Version 1.0 # Copyright (C) 2001 Stephen Carville # Unix and Network Administrator # Ace Flood USA # [EMAIL PROTECTED] # # 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: # Free Software Foundation, Inc. # 59 Temple Place, Suite 330, # Boston, MA 02111-1307 USA use strict; # use DB_File; # paths to amanda directories my $PREFIX = "/usr/local"; my $SBIN = "$PREFIX/sbin"; my $BIN = "$PREFIX/bin"; my $LIBEXEC = "$PREFIX/libexec"; # debugging files -- set DEBUGDIR to "" to disable debugging output my $DEBUGDIR = "/tmp/amanda"; my $DBGFILE = "changer.debug"; # set a conservative path. All commands are full paths so an empty path # would be OK my $PATH = "/bin:/sbin"; # commands -- change these to suit your configuration my $MT = "/usr/bin/mt"; my $MTX = "/usr/local/sbin/mtx"; my $AMGETCONF = "$SBIN/amgetconf"; my $MAILER = "/usr/bin/mailx"; # ----------- end of user configuration -------------- # exit codes my ($SUCCESS,$ERROR,$BADERROR) = (0,1,2); my ($cmd,$parm,$line,$rtn,$status); my ($tapedev,$changerdev,$changerfile,$mailto); my ($cleanfile,$accessfile,$slotfile,$labelfile,$dbgfile); # careful! - global! my ($firstslot,$lastslot,$cleanslot,$havereader); if (defined $ARGV[0]) {$cmd = $ARGV[0];} else {$cmd="";}; if (defined $ARGV[1]) {$parm = $ARGV[1];} else {$parm = "";}; # get rid of any leading '-' in the command $cmd =~ s/-//; # print "$cmd,$parm\n"; # set the execution path $ENV{PATH} = $PATH; # get some values from the configuration file -- careful these are # global $tapedev = `$AMGETCONF tapedev`; $tapedev =~ s/\n//; $changerdev = `$AMGETCONF changerdev`; $changerdev =~ s/\n//; $changerfile = `$AMGETCONF changerfile`; $changerfile =~ s/\n//; $mailto = `$AMGETCONF mailto`; $mailto =~ s/\n//; # mtx doesn't always need the changer device but it doesn't hurt $MTX .= " -f $changerdev"; # OTOH, mt usually does need the tape device $MT .= " -f $tapedev"; # set the debug file if ( -d $DEBUGDIR) { $dbgfile = "$DEBUGDIR/$DBGFILE"; } else { $dbgfile = "/dev/null"; } # this makes it easier to tell what happened debugmsg ("=========="); # print "$tapedev,$changerdev,$changerfile\n"; # I only use barcodes and conf but I include the rest JIC. $cleanfile = "$changerfile-clean"; $accessfile = "$changerfile-access"; $slotfile = "$changerfile-slot"; $labelfile = "$changerfile-barcodes"; $changerfile .= ".conf"; # get values from the changerfile open FILE, $changerfile or die "$BADERROR: Unable to open $changerfile"; foreach $line (<FILE>) { $line =~ s/\n//; $line =~ s/\s+//g; if ($line =~ m/^\#/ || $line eq "") { next; } $line =~ m/(\w+)=(\d+)/; # no tricky way to do this? if ($1 eq "firstslot") {$firstslot = $2; next; } if ($1 eq "lastslot") {$lastslot = $2; next; } if ($1 eq "cleanslot") {$cleanslot = $2; next; } if ($1 eq "havereader") {$havereader = $2; next; } } # print "$firstslot,$lastslot,$cleanslot,$havereader\n"; # branch to the appropiate subroutine # how are we callled? slot, info, reset, eject, label, search, clean if ($cmd eq "slot") { ($rtn,$status) = slot ($parm); print "$status\n"; exit $rtn; } if ($cmd eq "info") { ($rtn,$status) = info ($parm); print "$status\n"; exit $rtn; } if ($cmd eq "reset") { ($rtn,$status) = rset ($parm); print "$status\n"; exit $rtn; } if ($cmd eq "eject") { ($rtn,$status) = eject ($parm); print "$status\n"; exit $rtn; } if ($cmd eq "label") { ($rtn,$status) = label ($parm); print "$status\n"; exit $rtn; } if ($cmd eq "search") { ($rtn,$status) = search ($parm); print "$status\n"; exit $rtn; } if ($cmd eq "clean") { ($rtn,$status) = clean ($parm); print "$status\n"; exit $rtn; } # if get this far cmd is no good print "Unkown option: $cmd\n"; exit $BADERROR; # command subroutines # move a tape from a slot to the tape drive sub slot { my ($parm) = @_; my ($usedslot,$barcode,$loadslot); ($usedslot,$barcode) = status (); $loadslot = -1; debugmsg ("SLOT->load tape from slot $parm"); # what were we asked to do? # current -- return current loaded slot or load first slot if ($parm eq "current") { if ($usedslot < $firstslot) { $loadslot = $firstslot; } else { return $SUCCESS,"$usedslot $tapedev"; } return load ($loadslot); } # next or advance -- load the next tape or first slot if ($parm eq "next" or $parm eq "advance") { unload ($usedslot); $loadslot = $usedslot+1; if ($loadslot > $lastslot) { $loadslot = $firstslot; } return load ($loadslot); } # prev -- load the previous or last slot if ($parm eq "prev") { unload ($usedslot); $loadslot = $usedslot-1; if ($loadslot < $firstslot) { $loadslot = $lastslot; } return load ($loadslot); } # first -- load the first slot if ($parm eq "first") { unload ($usedslot); return load ($firstslot); } # last -- load the last slot if ($parm eq "last") { unload ($usedslot); return load ($lastslot); } # clean -- I do not support cleaning. I prefer to set it up # myself if ($parm eq "clean") { return clean($parm); } # check for a legitimate number unless ($parm =~ m/\d+/) { debugmsg ("SLOT->bad slot ID $parm"); return $ERROR,"0 Slot $parm is out of range ($firstslot -- $lastslot"; } if ($parm >= $firstslot && $parm <= $lastslot) { if ($parm == $usedslot) { return $SUCCESS,"$usedslot $tapedev"; } unload ($usedslot); return load ($parm); } else { return $BADERROR, "0 Slot $parm is out of range ($firstslot -- $lastslot)\n"; } return $BADERROR,"0 Unknown error!\n"; } # return some information about capabilities sub info { my ($parm) = @_; my ($usedslot,$barcode,$str); ($usedslot,$barcode) = status(); debugmsg("INFO -> current slot $usedslot, last slot $lastslot"); if ($usedslot < 0) { $usedslot = 0; } $str = "$usedslot $lastslot 1"; # someday check the tape library for havereader if ($havereader) { $str .= " 1"; } debugmsg ("INFO -> $str"); return $SUCCESS,"$str"; } # reset the tape library to a known state sub rset { my ($parm) = @_; my ($usedslot,$barcode); ($usedslot,$barcode) = status(); debugmsg ("RESET -> loading tape from slot $firstslot to $tapedev"); # first eject any tape already loaded unload ($usedslot); return load ($firstslot); } # eject the current tape sub eject { my ($usedslot,$barcode); ($usedslot,$barcode) = status(); debugmsg ("EJECT->unloading slot $usedslot"); return unload ($usedslot); } # add a label to the barcodes file sub label { my ($label) = @_; my ($usedslot,$barcode); my (%barcodes); ($usedslot,$barcode) = status(); %barcodes = load_labelfile(); debugmsg("Adding Barcode $barcode and amlabel $label for Slot $usedslot into $labelfile"); # if the label exists and is synced if (exists $barcodes{$label} && $barcode eq $barcodes{$label} ) { debugmsg("Barcode $barcode already synced for $label"); } else { # any other condition, add/overwrite it and save the new file $barcodes{$label} = $barcode; save_labelfile(%barcodes); } return $SUCCESS, "0 $usedslot $tapedev"; } # search for a tape in barcodes and load it into the drive sub search { my ($label) = @_; my (%labels,$slot,$err); debugmsg("SEARCH->searching for tape $label"); %labels = load_labelfile(); # if the label is in the file if (exists $labels{$label}) { $slot = findbybarcode($labels{$label}); } else { debugmsg("SEARCH->tape $label not found in barcode database"); errormsg("Error with Barcode reader","Tape $label not found in barcode database"); return $BADERROR,"tape $label not found in barcode database"; } # if the tape was not found in the changer if ($slot < 0) { debugmsg("SEARCH->tape $label not found in changer"); errormsg("Error with Barcode reader","Tape $label not found in changer"); return $BADERROR,"tape $label not found in changer"; } # tape is already loaded if ($slot == 0) { return $SUCCESS,"$tapedev"; } # otherwise load the tape debugmsg("SEARCH->tape $label found at slot $slot"); sleep (10); load($slot); return $SUCCESS,"$tapedev"; } # cleaning not supported yet sub clean { return $ERROR,"0 Cleaning not supported by driver\n"; } # unload a tape from the drive and replace it in its slot sub unload { my ($slot) = @_; my (@result); # we know that -1 is empty if ($slot < 0) { debugmsg ("UNLOAD->$tapedev already empty"); return $ERROR, "0 Drive was not loaded"; } @result = `$MTX unload $slot 2>&1`; if ($?) { debugmsg ("UNLOAD->error unloading slot $slot"); return $BADERROR,"0 @result"; } debugmsg ("UNLOAD->unloaded tape to slot $slot"); return $SUCCESS,"$slot $tapedev"; } # load a tape into the drive sub load { my ($slot) = @_; my (@result,$cntr,$off); debugmsg ("LOAD->loading tape from slot $slot"); @result = `$MTX load $slot 2>&1`; if ($?) { debugmsg ("LOAD->error in loading tape from slot $slot"); return $BADERROR,"$slot @result"; } # wait for drive to go online but not forever $cntr = 0; $off = ""; while ($off eq "") { @result = `$MT status 2>&1`; $off = grep (/offline/,@result); $cntr++; # is this the last try? if ($cntr > 10) { debugmsg ("LOAD->still offline at try # $cntr"); return $BADERROR,"$slot @result"; } sleep(10); } # now rewind the tape @result = `$MT rewind 2>&1`; sleep (1); # phew! debugmsg ("LOAD->$slot $tapedev"); return $SUCCESS,"$slot $tapedev"; } # # load the label database this is a space separated text file # but I return a hash to make manipulation easier. This should use # DB_File but not all systems have it installed so I follow the # KISS principle. returns %hash{$label}=$barcode sub load_labelfile { my (%labels,$line,$lbl,$bc); unless (-f $labelfile) { return %labels; } open LABEL, "$labelfile"; foreach $line (<LABEL>) { $line =~ s/\n//; ($lbl,$bc) = split /\s+/, $line; if (defined $lbl and defined $bc) { $labels{$lbl} = $bc; } } close LABEL; return %labels; } # save the labelfile -- this completely recreates the file! sub save_labelfile { my (%labels,$lbl) = @_; # clobber the existing file open LABEL, ">$labelfile"; foreach $lbl (keys %labels) { print LABEL "$lbl $labels{$lbl}\n"; } close LABEL; } # return the slot for a particular barcode sub findbybarcode { my ($bc) = @_; my (@result,$line,$slot); @result = `$MTX status 2>&1`; foreach $line (@result) { if ($line =~ /[\w\s]+ Element (0):Full \([\w\s\d]+\):VolumeTag = (.+)/) { if ($bc eq $2) { debugmsg("FINDBYBARCODE->Label $bc is at slot $1"); return $1; } } if ($line =~ /\w+\s+Element (\d+):Full :VolumeTag=(.+)/) { if ($bc eq $2) { debugmsg("FINDBYBARCODE->Label $bc is at slot $1"); return $1; } } } debugmsg("FINDBYBARCODE->Label $bc not found!"); return -1; } # return the slot and barcode of the loaded tape sub status { my (@result,$line,$stat,$el,$bc); # get the changer status @result = `$MTX status 2>&1`; # find the line with the tape drive in it foreach $line (@result) { if ($line =~ m/Data Transfer Element 0:(.+)/) { # preserve the slot number and barcode $stat = $1; last; } } #split the slot number from the barcode ($el,$bc) = split /:/,$stat; # get the loaded element if ($el =~ m/Full \(Storage Element (\d+) Loaded/) { $el = $1; } else { $el = -1; } if (defined $bc) { # get the loaded barcode if ($bc =~ m/VolumeTag = (.+)/) { $bc = $1; } else { $bc = -1; } } else { $bc = -1; } # print "$el,$bc\n"; return $el,$bc; } # add a debugging messsage to the debug file sub debugmsg { my ($message) = @_; open DBG, ">>$dbgfile"; print DBG $message; print DBG "\n"; close DBG; } # send an error message to mailto sub errormsg { my ($subject,$message) = @_; open PIPE, "| $MAILER -r \"$mailto\" -s \"$subject\" $mailto"; print PIPE $message; close PIPE; }