#! /usr/bin/python ''' =============================================================================================== Reads 9-track 1/2" tape from M4 Data mod. 9914R drive, with SCSI interface. Data assumed to be in SEG-C format. Note anomaly in file positioning using the 'mt' command. Tape drive operation is written as class library. Options: "-h", "--help" Print help message "-d=", "--dest=" Destination folder name "-a=" Append this number of files "-r=" Read this file no. "-n=" Number of repetitions if reading same file Dept. of Earth Science University of Bergen, Norway ------------------------------------------------------------------------------ Rev. Date By Description ------------------------------------------------------------------------------ 0.1 1 Nov 2006 O.M. First version. Based on segcREAD.C (M.S., 2004) ================================================================================================ ''' from string import split import select from time import time, gmtime, strftime, sleep import sys import os from os.path import isdir, isfile, join, getsize import getopt #***************************************************************** # C O N S T A N T S #***************************************************************** TAPE_A = '/dev/nst0' SEGC_HEADER_SIZE = 142 #***************************************************************** # C L A S S E S #***************************************************************** #----------------------------------------------------------------- # T a p e D r i v e C l a s s #----------------------------------------------------------------- class TapeDrive: #------------ "Constructor" --------------- def __init__(self, TapeDevice, LogFile): self.Device = TapeDevice self.Status = self.GetStatus() if (not self.Status['Error']) and self.Status['Online']: print '>>> Tape drive %s seems OK' % self.Device else: print '>>> Is tape drive %s OFFLINE?' % self.Device self.LogFile = LogFile self.Directory = LogFile.filename().split('/')[0] self.TempDataFile = join(self.Directory, '.temp.dat') self.ErrorLog = join(self.Directory, '.errors.txt') #---- Get tape drive status def GetStatus(self): Status = {} w, r, e = os.popen3('mt -f ' + self.Device + ' status') w.close() Error = e.readlines() e.close() if len(Error) != 0: print '>>> Error checking status of tape drive %s' % self.Device for line in Error: if len(line) != 1: print '>>> ---> ' + line.strip() Status['Error'] = True Status['Online'] = False Response = r.readlines() r.close() for line in Response: s = line.split() if 'file number' in line: Status['FileNo'] = s[-1] if 'block number' in line: Status['BlockNo'] = s[-1] if 'Tape block size' in line: Status['BlockSize'] = s[3] if 'ONLINE' in line: Status['Online'] = True Status['Error'] = False return Status #---- Print tape drive status ---------------------- def status(self): self.Status = self.GetStatus() print 'Tape drive status:' print ' Device: %s' % self.Device print ' BlockSize: %(BlockSize)s' % self.Status print ' Block number: %(BlockNo)s' % self.Status print ' File number: %(FileNo)s' % self.Status print ' Online:', self.Status['Online'] #---- Get current position pointer on tape -------------- def current_file(self): self.Status = self.GetStatus() return int(self.Status['FileNo']) #---- Move to specified location on tape ------------------------------- def move_to(self,target): if target > 0: current = int(self.current_file()) delta = (target - current) - 1 if target == 1: # Rewind tape os.system('mt -f ' + self.Device + ' rewind') elif delta > 0: # Forward to filenumber os.system('mt -f ' + self.Device + ' fsf %i' % (int(delta))) elif (delta < 0): # Rewind to filenumber os.system('mt -f %s bsf %s' % (self.Device, abs(delta))) self.Status = self.GetStatus() if self.Status['BlockNo'] == '-1': os.system('mt -f %s bsr %s 2>/dev/null' % (self.Device, '2')) # Reverse a bit, otherwise we'll read zero bytes on next attempt else: print '>>>>>> TAPE Error: Can not move to file number <= 0' #---- Read file from specified location on tape --------------------------- def read_file(self,FileNo): self.move_to(FileNo) os.system('rm %s 2> /dev/null' % self.TempDataFile) os.system('dd if=%s of=%s ibs=1024k count=3 2<<%s' % (self.Device, self.TempDataFile, join(self.Directory, self.ErrorLog))) f = open(self.TempDataFile, 'rb') Destination = '' Size = 0 if getsize(self.TempDataFile) == 0: print '>>>>>>>>>>> Read 0 bytes from tape' else: s = f.read(2) # Filename based on FLDR in SEGC file (positioned in two first bytes) FileName = '' # this FLDR is BCD coded for byte in s: FileName += '%02X' % ord(byte) # Build filename from these two bytes (convert to HEX as they are BCD coded) f.close() FileName += '.segc' Destination = join(self.Directory, FileName) os.system('cp %s %s' % (self.TempDataFile, Destination)) Size = getsize(Destination) self.LogFile.entry(FileNo, Destination, Size, 'QS-3416S') print 'No. %d : %s (%d)' % (FileNo, Destination, Size) return Destination, Size #----------------------------------------------------------------- # L o g F i l e C l a s s #----------------------------------------------------------------- class LogFileClass: #------------ "Constructor" --------------- def __init__(self, LogFileName): self.FileName = LogFileName self.FileStatus = {} if not isfile(LogFileName): #print 'Log file %s does not exist, creating' % LogFileName os.system('touch ' + LogFileName) else: LogFile = open(LogFileName) for line in LogFile.readlines(): #print line[:-1] if not (len(line) == 1 or (line[0] == '#')): FileInfo = {} try: s = line.split() FileSequenceNo = s[0] FileInfo['FileSequenceNo'] = FileSequenceNo FileInfo['FileName'] = s[1] FileInfo['FileSize'] = s[2] t = s[3:-1] FileInfo['FileDate'] = '' for item in t: FileInfo['FileDate'] += item + ' ' FileInfo['FileDate'] = FileInfo['FileDate'][:-1] # Get rid of excess SPACCE char FileInfo['Source'] = s[-1] self.FileStatus[int(FileSequenceNo)] = FileInfo except: print >> sys.stderr, 'Error parsing log file, exit' sys.exit(1) #------------ Print all entries in log file --------------- def printout(self): if len(self.FileStatus) == 0: print 'Empty log file' else: print 'From existing log file:' keys = self.FileStatus.keys() keys.sort() for key in keys: print '%(FileSequenceNo)03s %(FileName)s %(FileSize)s %(FileDate)s %(Source)s ' % (self.FileStatus[key]) #------------- Return the last item number in list ------------- def last_file(self): keys = self.FileStatus.keys() keys.sort() if len(keys) != 0: #print 'max fileno in logfile:', max(keys) return max(keys) else: return 0 #------------- Return log filename ------------- def filename(self): return self.FileName #------------- Modify existing or make new entry in logfile ------------- def entry(self, number, name, size, source): print '>>> Writing to log entry no.', number FileInfo = {} FileInfo['FileSequenceNo'] = str(number) FileInfo['FileName'] = name FileInfo['FileSize'] = size FileInfo['FileStatus'] = '' FileInfo['FileDate'] = strftime("%a %b %d %H:%M:%S %Y", gmtime()) FileInfo['Source'] = source self.FileStatus[number] = FileInfo f = open(self.FileName, 'w') keys = self.FileStatus.keys() keys.sort() for key in keys: f.write('%(FileSequenceNo)03s %(FileName)s %(FileSize)s %(FileDate)s %(Source)s\n' % (self.FileStatus[key])) f.close() #******************************************************************************************************* # # S t a r t o f p r o g r a m # #******************************************************************************************************* #------ Parse command line options try: opts, args = getopt.getopt(sys.argv[1:], "hd:a:r:n:", ["help", "dest="]) except getopt.error, msg: print msg print "for help use --help (or -h)" sys.exit(1) Append = False Repeat = 1 ReadFileNo = 0 Reel_Directory = '' No_Of_Files_To_Append = 0 for o, a in opts: if a[0] == '=': a = a[1:] if o in ("-h", "--help"): print __doc__ sys.exit(0) if o in ("-d", "--dest"): Reel_Directory = a if o in ("-a"): Append = True No_Of_Files_To_Append = int(a) if not No_Of_Files_To_Append >= 1: print 'Append: Only positive integer, larger then 0' sys.exit(1) if o in ("-r"): Append = False ReadFileNo = int(a) if not ReadFileNo >= 1: print 'Read file no: Only positive integer, larger then 0' sys.exit(1) if o in ("-n"): Append = False Repeat = int(a) if not Repeat >= 1: print 'No of loops: Only positive integer, larger then 0' sys.exit(1) if not Reel_Directory: print 'Not target directory' sys.exit(1) if (not Append) and (ReadFileNo == 0): print 'Missing parameters' sys.exit(1) if Append: print 'Appending %d files' % No_Of_Files_To_Append else: print 'Reading file no %d on tape' % ReadFileNo if Repeat != 0: print 'Repeat %d times' % Repeat #-------- Check reel directory, create if needed if not isdir(Reel_Directory): print 'Folder does not exist, creating' os.mkdir(Reel_Directory) #-------- Compose logfile name .... LogFileName = join(Reel_Directory, Reel_Directory + '.log') # Log file name = name of reel directory + '.log' #--------- Create logfile instance LogFile = LogFileClass(LogFileName) LogFile.printout() #--------- Create TapeDrive instance TapeDrive = TapeDrive(TAPE_A, LogFile) #-------------------------------------------------------------------------- # # M a i n l o o p # #-------------------------------------------------------------------------- if Append: LastFile = LogFile.last_file() CurrentFile = LastFile + 1 while CurrentFile <= (LastFile + No_Of_Files_To_Append): TapeDrive.read_file(CurrentFile) CurrentFile += 1 else: for n in range(Repeat): TapeDrive.read_file(ReadFileNo) sys.exit(0)