# Copyright 2000 Enhanced Software Technologies Inc.
# Released under Free Software Foundation's General Public License,
# Version 2 or above
#
# This is an example of how to parse the 'mtx' output from a scripting
# language. 
#
# Routine to call 'mtx' and read status, or
# whatever.
#
# We do this here rather than creating a Python "C" module because:
#  1) Reduces complexity of compile environment
#  2) Easier debugging.
#  3) More in keeping with the Unix philosophy of things.
#  4) Easier to add new functionality. 
#
#

import string
import os
import time # we can do some waiting here...

def readpipe(command):
    result=""
    
    infile=os.popen(command,"r")

    try:
        s=infile.read()
    except:
        s=""
        pass
    if not s:
        return None  # didn't get anything :-(. 
    result=result+s
    return result




# these are returned by the mtx.status routine:
class slotstatus:
    def __init__(self,slotnum,middle,barcode=None):
        middle=string.strip(middle)
        try:
            left,right=string.split(middle," ")
        except:
            left=middle
            right=None
            pass
        # Note: We will not be able to test this at the moment since the
        # 220 has no import/export port!
        if right=="IMPORT/EXPORT":
            self.importexport=1
        else:
            self.importexport=0
            pass
        self.slotnum=int(slotnum) # make sure it's an integer...
        if left=="Full":
            self.full=1
        else:
            self.full=None
            pass
        if not barcode:
            self.voltag=None
        else:
            l=string.split(barcode,"=",1)
            self.voltag=l[1]
            pass
        return
    pass

# Drive status lines have this format:
#Data Transfer Element 0:Full (Storage Element 10 Loaded):VolumeTag = B0000009H
#Data Transfer Element 1:Empty

class drivestatus:
    def __init__(self,slotnum,middle,barcode=None):
        self.slotnum=slotnum
        if middle=="Empty":
            self.full=None
            self.origin=None
            self.voltag=None
            return
        else:
            self.full=1
            pass

        # okay, we know we have a tape in the drive. 
        # split and find out our origin: we will always have
        # an origin, even if the #$@% mtx program had to dig one
        # out of the air:

        l=string.split(middle," ")
        self.origin=int(l[3])

        if not barcode:
            self.voltag=None # barcode of this element.
        else:
            l=string.split(barcode," ")
            self.voltag=string.strip(l[2])
            pass
        return
    pass

# This is the return value for mtx.status. 
class mtxstatus:
    def __init__(self):
        self.numdrives=None
        self.numslots=None
        self.numexport=None
        self.drives=[]  # a list of drivestatus instances.
        self.slots=[]   # a list of slotstatus instances
        self.export=[]  # another list of slotstatus instances
        return
    pass

# call 'mtx' and get barcode info, if possible:
# okay, we now have a string that consists of a number of lines.
# we want to explode this string into its component parts.
# Example format:
# Storage Changer /dev/sgd:2 Drives, 21 Slots
# Data Transfer Element 0:Full (Storage Element '5' Loaded) 
# Data Transfer Element 1:Empty
# Storage Element 1:Full :VolumeTag=CLNA0001
# Storage Element 2:Full :VolumeTag=B0000009
# Storage Element 3:Full :VolumeTag=B0000010
# ....
# What we want to do, then, is:
# 1) Turn it into lines by doing a string.split on newline.
# 2) Split the 1st line on ":" to get left and right.
# 3) Split the right half on space to get #drives "Drives," #slots
# 4) process the drives (Full,Empty, etc.)
# 5) For each of the remaining lines: Split on ':'
# 6) Full/Empty status is in 2)
#
configdir="/opt/brupro/bin"  # sigh. 

def status(device):
    retval=mtxstatus()
    command="%s/mtx -f %s status" % (configdir,device)
    result=readpipe(command)
    if not result:
        return None  # sorry! 
    # now to parse:

    try:
        lines=string.split(result,"\n")
    except:
        return None # sorry, no status!
    
    # print "DEBUG:lines=",lines

    try:
        l=string.split(lines[0],":")
    except:
        return None # sorry, no status!
    
    # print "DEBUG:l=",l
    leftside=l[0]
    rightside=l[1]
    if len(l) > 2:
        barcode=l[3]
    else:
        barcode=None
        pass
    t=string.split(rightside)
    retval.numdrives=int(t[0])
    retval.numslots=int(t[2])
    
    for s in lines[1:]:
        if not s:
            continue # skip blank lines!
        #print "DEBUG:s=",s
        parts=string.split(s,":")
        leftpart=string.split(parts[0])
        rightpart=parts[1]
        try:
            barcode=parts[2]
        except:
            barcode=None
            pass
        #print "DEBUG:leftpart=",leftpart
        if leftpart[0]=="Data" and leftpart[1]=="Transfer":
            retval.drives.append(drivestatus(leftpart[3],rightpart,barcode))
            pass
        if leftpart[0]=="Storage" and leftpart[1]=="Element":
            element=slotstatus(leftpart[2],rightpart,barcode)
            if element.importexport:
                retval.export.append(element)
            else:
                retval.slots.append(element)
                pass
            pass
        continue

    return retval

# Output of a mtx inquiry looks like:
#
#Product Type: Medium Changer
#Vendor ID: 'EXABYTE '
#Product ID: 'Exabyte EZ17    '
#Revision: '1.07'
#Attached Changer: No
#
# We simply return a hash table with these values { left:right } format. 

def mtxinquiry(device):
    command="%s/mtx -f %s inquiry" % (configdir,device)
    str=readpipe(command) # calls the command, returns all its data.

    str=string.strip(str)
    lines=string.split(str,"\n")
    retval={}
    for l in lines:
        # DEBUG #
        l=string.strip(l)
        print "splitting line: '",l,"'"
        idx,val=string.split(l,':',1)
        val=string.strip(val)
        if val[0]=="'":
            val=val[1:-1] # strip off single quotes, sigh. 
            pass 
        retval[idx]=val
        continue
    return retval

# Now for the easy part:

def load(device,slot,drive=0):
    command="%s/mtx -f %s load %s %s >/dev/null " % (configdir,device,slot,drive)
    status=os.system(command)
    return status

def unload(device,slot,drive=0):
    command="%s/mtx -f %s unload %s %s >/dev/null " % (configdir,device,slot,drive)
    return os.system(command)

def inventory(device):
    command="%s/mtx -f %s inventory >/dev/null " % (configdir,device)
    return os.system(command)

def wait_for_inventory(device):
    # loop while we have an error return...
    errcount=0
    while inventory(device):
        if errcount==0:
            print "Waiting for loader '%s'" % device
            pass
        time.sleep(1)
        try:
            s=status(device)
        except:
            s=None
            pass
        if s:
            return 0 # well, whatever we're doing, we're inventoried :-(
        errcount=errcount+1
        if errcount==600:   # we've been waiting for 10 minutes :-(
            return 1 # sorry!
        continue
    return 0  # we succeeded!

# RCS REVISION LOG:
# $Log: mtx.py,v $
# Revision 1.1.1.1  2001/06/05 17:10:51  elgreen
# Initial import into SourceForge
#
# Revision 1.2  2000/12/22 14:17:19  eric
# mtx 1.2.11pre1
#
# Revision 1.14  2000/11/12 20:35:29  eric
# do string.strip on the voltag
#
# Revision 1.13  2000/11/04 00:33:38  eric
# if we can get an inventory on the loader after we send it 'mtx inventory',
# then obviously we managed to do SOMETHING.
#
# Revision 1.12  2000/10/28 00:04:34  eric
# added wait_for_inventory command
#
# Revision 1.11  2000/10/27 23:27:58  eric
# Added inventory command...
#
# Revision 1.10  2000/10/01 01:06:29  eric
# evening checkin
#
# Revision 1.9  2000/09/29 02:49:29  eric
# evening checkin
#
# Revision 1.8  2000/09/02 01:05:33  eric
# Evening Checkin
#
# Revision 1.7  2000/09/01 00:08:11  eric
# strip lines in mtxinquiry
#
# Revision 1.6  2000/09/01 00:05:33  eric
# debugging
#
# Revision 1.5  2000/08/31 23:46:01  eric
# fix def:
#
# Revision 1.4  2000/08/31 23:44:06  eric
# =->==
#
