A while ago, we upgraded all 50 backup tapes in the tape library at work from LTO-2 to LTO-3. This of course meant unpacking, inserting and labeling those tapes, both physically as well as electronically. While the physical labeling could not be automated1), I finally sat down to at least make the electronic labeling using amanda's amlabel a little bit less unworthy of a sysadmin
The result was a small python script2) capable of bulk labeling a bunch of tapes based on a formatstring pattern of the label format that at least works perfectly fine in our environment – YMMV
I decided to post it here nevertheless as it might be of help for somebody out there besides us3). Some basic usage instructions, the code and a download link follow.
Usage is quite simple. Given your labels are something like TAPE01 or BACKUP_042, the formatstring would be TAPE%02i or BACKUP_%03i. So if you e.g. want to label five tapes using the above BACKUP format (and amanda's BACKUP config), starting with tape 42 which is located in physical slot 23 of your library, the tapelabel call would be tapelabel -f 23 -c 5 BACKUP BACKUP_%03i 42. For more info, see tapelabel –help:
Usage: tapelabel [options] config formatstring number
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-v, --verbose verbose output, use multiple times for spam
-q, --quite suppress output, use multiple times for silence
-f FIRSTSLOT, --firstslot=FIRSTSLOT
specifies slot of first tape to label [default: '1']
-l LASTSLOT, --lastslot=LASTSLOT
specifies slot of last tape to label
-c COUNT, --count=COUNT
specifies count of tapes to label [default: 'none']
-F, --force force writing of label
--dry-run simulates run
/root/bin/tapelabel
#!/usr/bin/python import os import sys import commands from optparse import OptionParser SLOT_COMMAND = '/usr/sbin/changer -slot %i' WRITE_COMMAND = '/usr/sbin/amlabel %s %s' WRITE_COMMAND_FORCE = '/usr/sbin/amlabel -f %s %s' READ_COMMAND = '/usr/bin/readlabel /dev/rmt/current' options = config = fs = startValue = None def change_slot(slot): dePrint("Changing to slot %i..." % slot) if (not options.dryrun): (status, output) = commands.getstatusoutput(SLOT_COMMAND % slot) if (status != 0): print >>sys.stderr, "Error while changing tape: %s" % output sys.exit(-1) dePrint("\tDone.", 2) def write_label(val): global options, config, fs label = fs % val dePrint("Writing label '%s'..." % label) if (not options.dryrun): if (options.force): (status, output) = commands.getstatusoutput(WRITE_COMMAND_FORCE % (config, label)) else: (status, output) = commands.getstatusoutput(WRITE_COMMAND % (config, label)) if (status != 0): if (status == 127): print >>sys.stderr, "amlabel reported '%s'. You might want to use -f to override this and force the label." % output sys.exit(-2) else: print >>sys.stderr, "Error while labeling: '%s'" % output sys.exit(-3) dePrint("\tDone.", 2) def verify_label(val): global options, config, fs label = fs % val dePrint("Verifying label '%s'..." % label) if (not options.dryrun): (status, output) = commands.getstatusoutput(READ_COMMAND) if (status != 0): print >>sys.stderr, "Error while reading label: '%s'" % output sys.exit(-4) current_label = output.split()[-1] dePrint("\tRead label '%s'." % current_label, 2) if (not options.dryrun): return (label == current_label) else: dePrint("\tRead label '%s'." % label, 2) return True def main(): global options, config, fs, startValue setup() try: slot = options.firstSlot for i in range(options.count): change_slot(slot) write_label(startValue + i) if (verify_label(startValue + i)): slot = slot + 1 else: print >>sys.stderr, "Verification of label failed." sys.exit(-4) except KeyboardInterrupt: dePrint("# KeyboardInterrupt caught", level=0) def setup(): global options, fs, config,startValue minVer = [2, 4] ver = sys.version_info[0:2] if not (ver[0] > minVer[0] or (ver[0] == minVer[0] and ver[1] >= minVer[1])): print >>sys.stderr, "This program requires Python >=%s, aborting." % ",".join(map(str, minVer)) sys.exit(-1) ## argument parsing setup # parser = OptionParser(version = "%prog 0.1") parser.set_usage("%prog [options] config formatstring number") parser.add_option("-v", "--verbose", action = "count", dest = "verbose", default = 0, help = "verbose output, use multiple times for spam" ) parser.add_option("-q", "--quite", action = "count", dest = "quite", default = 0, help = "suppress output, use multiple times for silence" ) parser.add_option("-f", "--firstslot", type = "int", dest = "firstSlot", default = 1, help = "specifies slot of first tape to label " + "[default: '%default']" ) parser.add_option("-l", "--lastslot", type = "int", dest = "lastSlot", default = None, help = "specifies slot of last tape to label" ) parser.add_option("-c", "--count", type = "int", dest = "count", default = None, help = "specifies count of tapes to label " + "[default: '%default']" ) parser.add_option("-F", "--force", action = "store_true", dest = "force", help = "force writing of label" ) parser.add_option("--dry-run", action = "store_true", dest = "dryrun", help = "simulates run" ) (options, fs) = parser.parse_args() if (len(fs) == 0): parser.error("You need to supply a format string.") elif (len(fs) != 3): parser.error("Need config, formatstring and start value.") else: (config, fs, startValue) = fs startValue = int(startValue) if (not options.lastSlot and not options.count): parser.error("You need to supply either a count of tapes to label or the number of the last slot which to label.") elif (not options.lastSlot and options.count): options.lastSlot = options.firstSlot + options.count - 1 elif (options.lastSlot and not options.count): options.count = options.lastSlot - options.firstSlot + 1 elif (options.lastSlot and options.count and not (options.count == options.lastSlot - options.firstSlot + 1)): parser.error("Start slot plus tape count does not equal end slot. Please decide on either count or endslot.") # verbosity if options.quite: options.verbose = -options.quite def dePrint(str, level=1): """prints (debug) messages""" if (options.verbose - level) >= 0: print str if __name__ == "__main__": main()