@python -x %~f0 %* & goto :EOF

import os
import string
import sys
import time
import re
import win32file
import win32pipe
from stat import *

from pywintypes import FALSE, TRUE
from win32file import GENERIC_WRITE, FILE_SHARE_READ, OPEN_ALWAYS
from re import IGNORECASE

EXIT_SUCCESS = 0
EXIT_FAILURE = 1

ARAXIHOME        = os.environ['ARAXIHOME']
COMPUTERNAME     = os.environ['COMPUTERNAME']
_AraxiCurrentWas = 'AraxiCurrentWas'    # file used to indicate when last snapshot was taken
_messageList     = []
_enableFilter    = FALSE
_scriptFolder    = os.getcwd()
_defaultLogFolder = os.path.join ( ARAXIHOME, 'CreoAraxi', 'System', 'Log' )
_defaultAraxiLogFileRegExpr = 'Araxi[0-9]*.log'
_resultFolder    = os.path.join( _scriptFolder, 'Snapshot_Results' )
_logFileName     = COMPUTERNAME + '_Info.txt'
_utilityList     = []
_maxLogSizeMB    = 100
_logFolder       = ''
_logFileRegExpr  = '.*'
_snapshotAraxiLogs = TRUE

HELP_MESSAGE = \
'''\
Araxi diagnostic log snapshot utility version 1.1.
       Snapshot will run PSnap, TList -t and save the output to a log file and place the file
       in the folder Snapshot_Results. The log file name is in the format: <computer_name>_INFO.txt.
       The Snapshot_Results folder will be a subdirectory of the folder where the Snapshot utility
       resides. Snapshot will also save the diagnostics (Araxi) logs in Snapshot_Results.
       If you run snapshot again, it will only save the new un-saved logs created
       since the last snapshot. All the information gathered at the time of the snapshot
       will be zipped into a file where the file name is in the format:
       <computername>_YYYY-MM-DD_HH_MI_SS.zip.
       
       

Usage: Snapshot [-find <message_text1>] [-find <message_text2>] 
                [-run <utility1>] [-run <utility2>] [-file <file_name>] [-scan <log_file_path>]

       Note: Running Snapshot without any options will run PSnap and TList -t then zip all the new log files.
                                               
       -find <message_text>              \tOnly gather logs if <message_text> is found.
                                         \tYou can specify more than one message if desired.
       -scan <log_file_path>             \tLog folder to scan. Default is %AraxiHome%\CreoAraxi\System\Logs.
       -re <regular_expression>          \tFilter log file names using regular expression.
       -run <utility>                    \tRun an additional utility.
                                         \tYou can run more than one utility if desired.
       -save <snapshot_folder>           \tSave snapshots to the folder <snapshot_folder>.
       -file <file_name>                 \tLog output from utilities to <file_name>. Default is %COMPUTERNAME%_Info.txt.
       -size <max_size_in_MB>            \tMaximum disk space (in MB) used for snapshots. Default is 100 MB.
       -nolog                            \tDo not take a snapshot of the Araxi logs (just log the ouput of -run commands).
       -? -h -help                       \tHelp screen.
'''


def main():
    
    # get command line arguments
    GetArgs()
    if not os.path.exists( _resultFolder ):
        os.mkdir( _resultFolder )

    utilityLog = os.path.join( _resultFolder, _logFileName )

    print 'Gathering information, please wait.....'

    # Run utilities and capture output to a log file and
    # add the log file to the list of items to zip.
    zipList = [] 
    if _utilityList != []:
        print 'Results will be logged in %s.' % utilityLog
        RunUtility( _utilityList, utilityLog )
        zipList.append( utilityLog )

    if _logFolder == '':
        logFolder = _defaultLogFolder
        logFileRegExpr = _defaultAraxiLogFileRegExpr
    else:
        logFolder = _logFolder
        logFileRegExpr = _logFileRegExpr

    # Take a snapshot and if we have new Araxi logs,
    # add the log files to the list of items to zip.
    if _snapshotAraxiLogs == TRUE:
        zipList.extend( TakeSnapshot( logFolder, logFileRegExpr, _resultFolder ) )

    # Zip the files
    if not zipList == []:
        ZipFiles( zipList, _resultFolder )
        returnCode = 0
    else:
        print 'No files to zip!'
        returnCode = 1

    # Limit the directory size so we don't fill up the disk.
    # The oldest files will be deleted first until the directory
    # size drops below the threshold.
    # The default size limit is 100 MB. 
    LimitDirectorySize( _resultFolder, _maxLogSizeMB*1024*1024, '.*\.zip$' )

    # Return 0 if we had files to zip, 1 if nothing has been zipped.
    sys.exit( returnCode )

def GetArgs():

    global _enableFilter
    global _messageList
    global _resultFolder
    global _utilityList
    global _logFileName
    global _maxLogSizeMB
    global _logFolder
    global _logFileRegExpr
    global _snapshotAraxiLogs

    argc = len( sys.argv )
    i = 1
    while i < argc:
        arg = sys.argv[i]
        argUC = string.upper( arg )
        if argUC in [ '-?', '-H', '-HELP' ]:
            PrintUsageAndExit()
        elif argUC == '-FIND':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _messageList.append( sys.argv[i] )
            _enableFilter = TRUE
        elif argUC == '-RUN':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _utilityList.append( sys.argv[i] )
        elif argUC == '-SAVE':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _resultFolder = sys.argv[i]
        elif argUC == '-NORUN':
            _utilityList = []
        elif argUC == '-FILE':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _logFileName = sys.argv[i]
        elif argUC == '-SIZE':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _maxLogSizeMB = int( sys.argv[i] )
        elif argUC == '-SCAN':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _logFolder = sys.argv[i]
        elif argUC == '-RE':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _logFileRegExpr = sys.argv[i]

        elif argUC == '-NOLOG':
            _snapshotAraxiLogs = FALSE


        # To be compatible with older versions, allow these options

        elif argUC == '-LOG':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _resultFolder = sys.argv[i]

        elif argUC == '-MESSAGE':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _messageList.append( sys.argv[i] )
            _enableFilter = TRUE
        elif argUC == '-UTILITY':
            i = i + 1
            if ( i >= argc ):
                PrintUsageAndExit( 'Option %s requires an additional argument' % arg, EXIT_FAILURE )
            _utilityList.append( sys.argv[i] )

        else:
            PrintUsageAndExit( 'Unknown option %s.' % arg, EXIT_FAILURE )
        i = i + 1
  


def RunUtility( utilityList, utilityLogFile ):
    myLog = Log( utilityLogFile )
    for utility in utilityList:
        print 'Running: %s' % utility
        commandLine = utility
        myLog.LogMessage( commandLine )
        ( In, Out ) = win32pipe.popen4( commandLine, 't' )
        myLog.LogMessage( Out.read() )
        In.close()
        Out.close()
    myLog.Close()


def ZipFiles( zipFileList, zipFileDestination ):
    print 'Zipping files, please wait.....'

    myZip = Zip( zipFileDestination )

    # zip file(s) containing snapshot info
    for file in zipFileList:
        myZip.ZipFile( file )


def TakeSnapshot( scanFolder, scanFileRegExpr, snapshotFolder ):

    # find when we made our last snapshot
    mySnapshot = Snapshot( snapshotFolder )
    lastSnapshot = mySnapshot.GetLastSnapshotTime( )
    mostRecentLog = lastSnapshot

    # find all log files newer than last snapshot
    araxiLogList = GetNewLogs( scanFolder, scanFileRegExpr, lastSnapshot )

    for file in araxiLogList:
        modTime = os.stat ( file ) [ST_MTIME]            
        # find the most recent log file scanned
        if modTime > mostRecentLog:
            mostRecentLog = modTime
    
    # check log files for message string if user requested it
    messageFound = FALSE
    if _enableFilter == TRUE:
        for file in araxiLogList:
            # search for message in the new log files
            if FindMessage( file, _messageList ):
                messageFound = TRUE
                # stop searching on the first occurrence
                break;

    # if filtering is on and we don't find the message text in the log
    # we will return an empty list
    if ( _enableFilter == TRUE and messageFound == FALSE ):
        araxiLogList = []

    # record this shapshot time
    mySnapshot.SetSnapshotTime( mostRecentLog )
    return araxiLogList


def GetNewLogs( logFolder, logFileRegExpr, fromSnapshotTime ):

    newAraxiLogs = []
    if not os.path.exists( logFolder ):
        print 'Path %s does not exist.' % logFolder
    else:
        araxiLogs = os.listdir( logFolder )
        logFileRegExprCI = re.compile( logFileRegExpr, IGNORECASE)
        for file in araxiLogs:
            search = logFileRegExprCI.match( file )
#            search = re.search( logFileRegExprCI,file )
            if ( search ):
                logFile = os.path.join( logFolder, file )
                modTime = os.stat ( logFile ) [ST_MTIME]
                if modTime > fromSnapshotTime:
                    newAraxiLogs.append( logFile )
    return newAraxiLogs


def FindMessage( logFile, messageList ):
    resultMsg = 'Message found.'
    commandLine = 'findstr /I /M '
    # append a list of the messages to search for to the command line
    for messageString in _messageList:
        commandLine = commandLine + '/C:\"'+messageString+'\" '
    commandLine = commandLine + logFile + ' > NUL & IF NOT errorlevel 1 ECHO '+ resultMsg

    (In, Out) = win32pipe.popen4( commandLine, 't' )
    msgFound = 0
    while 1:
        line = Out.readline()
        if line == '':
            break;
        search = re.search ( resultMsg, line )
        if search:
            msgFound = 1

    In.close()
    Out.close()
    return msgFound

class Snapshot:
    def __init__( self, snapshotFolder ):
        self.snapshotMarker = os.path.join( snapshotFolder, _AraxiCurrentWas )

    def SetSnapshotTime( self, time ):
        modTime = time
        accessTime = modTime
        os.utime( self.snapshotMarker, [accessTime, modTime] )

    def GetLastSnapshotTime( self ):
        # record snapshot by setting the modification time of the file called AraxiCurrentWas
        # saved in the same folder as the zip files
    
        if not os.path.exists( self.snapshotMarker ):
            # create the AraxiCurrentWas file if it doesn't exist
            # and set the modification time to some time in the past
            lastZippedLog = open( self.snapshotMarker, 'w' )
            lastZippedLog.close()
            modTime = 1000000000 # some time long ago
            accessTime = modTime
            os.utime( self.snapshotMarker, [accessTime,modTime] )

        snapshotMarkerModTime = os.stat ( self.snapshotMarker ) [ST_MTIME]
        return snapshotMarkerModTime

class Zip:
    def __init__( self , zipFolder ):
        dateTimeFormat = '_%Y-%m-%d_%H.%M.%S'
        dateTime = time.strftime( dateTimeFormat, time.localtime( time.time( ) ) )
        self.zipFileName = COMPUTERNAME + dateTime + '.zip'
        self.zipFilePath = os.path.join( zipFolder, self.zipFileName )
        print 'Zip file for this snapshot is: %s' % self.zipFilePath
 

    def ZipFile( self, fileToZip ):
        commandLine = 'zip.exe '+ self.zipFilePath + ' ' + fileToZip
        print commandLine
        (In, Out) = win32pipe.popen4( commandLine, 't' )
        while 1:
            line = Out.readline()
            if line == '':
                break;
            print line
        Out.close()
        In.close()

class Log:
    def __init__( self, fullLogFileName ):
        self.logFile = open( fullLogFileName , 'w' )

    def LogMessage( self, msg ):
        self.logFile.write( msg )
    #    print msg

    def Close( self ):
        self.logFile.close()

# Limit folder size. Delete files by date until size is below maxSize.
def LimitDirectorySize( folder,  maxSize, filter = '.*', ):
    fileInfo = []
    fileList = os.listdir( folder )
    dirSizeBytes = 0
    for file in fileList:
        if re.search( filter, file ):
            fileSizeBytes = os.stat( os.path.join( folder, file ) ) [ST_SIZE]
            fileModTime = os.stat( os.path.join( folder, file ) ) [ST_MTIME]
            dirSizeBytes = dirSizeBytes + fileSizeBytes
            fileInfo.append( os.path.join( folder, file ), fileModTime, fileSizeBytes )
#    print 'dirSizeBytes is %s' % dirSizeBytes

    if dirSizeBytes > maxSize:
        fileInfo.sort( lambda x,y:cmp(x[1],y[1] ) )

        remainingBytes = dirSizeBytes
        for ( fileName, fileModTime, fileSizeBytes ) in fileInfo:
            os.unlink( fileName )
            remainingBytes = remainingBytes - fileSizeBytes
            print maxSize
            print remainingBytes

            print fileName
            print fileModTime
            print fileSizeBytes
            if remainingBytes < maxSize:
                break;



def PrintUsageAndExit( errmsg='', exitcode=EXIT_SUCCESS ):
    if errmsg:
        print errmsg
        print
    print HELP_MESSAGE
    sys.exit( exitcode )
    


if __name__ == '__main__':
    main()