--- /dev/null
+#!/usr/bin/python2.6\r
+\r
+'''A process wrapper class that maintains the text output and execution status of a process\r
+or a list of other process wrappers which carry such data.'''\r
+\r
+import os\r
+import sys\r
+import traceback\r
+\r
+def readText(textFile):\r
+ if( textFile != "" ):\r
+ fp = open(textFile, 'rb')\r
+ # Create a text/plain message\r
+ text = (fp.read())\r
+ fp.close()\r
+ return text\r
+# readText\r
+\r
+def writeText(text, textFile):\r
+ if( textFile != "" ):\r
+ fp = open(textFile, 'wb')\r
+ # Create a text/plain message\r
+ fp.write(text)\r
+ fp.close()\r
+ return text\r
+# readText\r
+\r
+class Process:\r
+ "A process with logged output"\r
+\r
+ def __init__(self, description=None, cmd=None, args=[], cwd=None, env=None, batchWrapper=False):\r
+ "Initialize the standard class variables"\r
+ self.cmd = cmd\r
+ if not description:\r
+ self.description = cmd\r
+ else:\r
+ self.description = description\r
+ self.status = None\r
+ self.args = args\r
+ self.start = None\r
+ self.end = None\r
+ self.log = []\r
+ self.echo = True\r
+ self.cwd = cwd\r
+ self.env = env\r
+ self.batchWrapper = batchWrapper\r
+ self.processKeys = []\r
+ # __init__\r
+\r
+ def getElapsedSeconds(self):\r
+ import math\r
+ if self.end and self.start:\r
+ delta = (self.end - self.start)\r
+ formatted = "%s.%s" % (delta.days * 86400 + delta.seconds, int(math.floor(delta.microseconds/1e3)))\r
+ else:\r
+ formatted = None\r
+ return formatted\r
+ # getElapsedtime\r
+\r
+ def writeKey(self, writeDict, key=None, value=None, startStop=None):\r
+ "Write a key, value pair in a supported format"\r
+ if key != None and (value != None or startStop != None):\r
+ indent = '\t'*writeDict['indentationLevel']\r
+ if writeDict['format'] == 'xml':\r
+ if startStop == 'start':\r
+ writeDict['logHandle'].write( "%s<%s>\n" % (indent, key) )\r
+ elif startStop == 'stop':\r
+ writeDict['logHandle'].write( "%s</%s>\n" % (indent, key) )\r
+ else:\r
+ writeDict['logHandle'].write( "%s<%s>%s</%s>\n" % (indent, key, value, key) )\r
+ else: # writeDict['format'] == 'txt':\r
+ writeDict['logHandle'].write( "%s%40s : %s\n" % (indent, key, value) )\r
+\r
+ def writeLogHeader(self, writeDict):\r
+ import platform\r
+\r
+ # Retrieve operating environment information\r
+ user = None\r
+ try:\r
+ user = os.getlogin()\r
+ except:\r
+ try:\r
+ user = os.getenv("USERNAME")\r
+ if user == None:\r
+ user = os.getenv("USER")\r
+ except:\r
+ user = "unknown_user"\r
+ try:\r
+ (sysname, nodename, release, version, machine, processor) = platform.uname()\r
+ except:\r
+ (sysname, nodename, release, version, machine, processor) = ("unknown_sysname", "unknown_nodename", "unknown_release", "unknown_version", "unknown_machine", "unknown_processor")\r
+ try:\r
+ hostname = platform.node()\r
+ except:\r
+ hostname = "unknown_hostname"\r
+\r
+ self.writeKey(writeDict, 'process', None, 'start' )\r
+ writeDict['indentationLevel'] += 1\r
+\r
+ self.writeKey(writeDict, 'description', self.description )\r
+ self.writeKey(writeDict, 'cmd', self.cmd )\r
+ if self.args: self.writeKey(writeDict, 'args', ' '.join(self.args) )\r
+ self.writeKey(writeDict, 'start', self.start )\r
+ self.writeKey(writeDict, 'end', self.end )\r
+ self.writeKey(writeDict, 'elapsed', self.getElapsedSeconds() )\r
+\r
+ self.writeKey(writeDict, 'user', user )\r
+ self.writeKey(writeDict, 'sysname', sysname )\r
+ self.writeKey(writeDict, 'nodename', nodename )\r
+ self.writeKey(writeDict, 'release', release )\r
+ self.writeKey(writeDict, 'version', version )\r
+ self.writeKey(writeDict, 'machine', machine )\r
+ self.writeKey(writeDict, 'processor', processor )\r
+\r
+ if len(self.processKeys) > 0:\r
+ self.writeKey(writeDict, 'processKeys', None, 'start' )\r
+ for pair in self.processKeys:\r
+ (key, value) = pair\r
+ writeDict['indentationLevel'] += 1\r
+ self.writeKey(writeDict, key, value )\r
+ writeDict['indentationLevel'] -= 1\r
+ self.writeKey(writeDict, 'processKeys', None, 'stop' )\r
+\r
+ self.writeKey(writeDict, 'status', self.status )\r
+ # writeLogHeader\r
+\r
+ def writeLogFooter(self, writeDict):\r
+ writeDict['indentationLevel'] -= 1\r
+ self.writeKey(writeDict, 'process', None, 'stop' )\r
+ # writeLogFooter\r
+\r
+ def writeLog(self, logHandle=sys.stdout, indentationLevel=0,format='xml'):\r
+ "Write logging information to the specified handle"\r
+ \r
+ writeDict = {}\r
+ writeDict['logHandle'] = logHandle\r
+ writeDict['indentationLevel'] = indentationLevel\r
+ writeDict['format'] = format\r
+ \r
+ if logHandle:\r
+ self.writeLogHeader(writeDict)\r
+ \r
+ if self.log:\r
+ self.writeKey(writeDict, 'output', None, 'start' )\r
+ if format == 'xml':\r
+ logHandle.write( "<![CDATA[\n" )\r
+ for line in self.log:\r
+ logHandle.write( '%s%s\n' % ("", line) )\r
+ if format == 'xml':\r
+ logHandle.write( "]]>\n" )\r
+ self.writeKey(writeDict, 'output', None, 'stop' )\r
+\r
+ self.writeLogFooter(writeDict)\r
+ # writeLog\r
+\r
+ def writeLogToDisk(self, logFilename=None, format='xml', header=None):\r
+ if logFilename: \r
+ try:\r
+ # This also doesn't seem like the best structure...\r
+ # 3.1\r
+ try:\r
+ logHandle = open( logFilename, mode='wt', encoding="utf-8")\r
+ # 2.6\r
+ except:\r
+ logHandle = open( logFilename, mode='wt')\r
+ except:\r
+ print( "Couldn't open log : %s" % logFilename )\r
+ logHandle = None\r
+\r
+ if logHandle:\r
+ if header:\r
+ if format == 'xml':\r
+ logHandle.write( "<![CDATA[\n" )\r
+ logHandle.write( header )\r
+ if format == 'xml':\r
+ logHandle.write( "]]>\n" )\r
+ self.writeLog(logHandle)\r
+ logHandle.close()\r
+ # writeLogToDisk\r
+\r
+ def logLine(self, line):\r
+ "Add a line of text to the log"\r
+ self.log.append( line.rstrip() )\r
+ if self.echo:\r
+ print( "%s" % line.rstrip() )\r
+ # logLine\r
+\r
+ def execute(self):\r
+ "Execute this process"\r
+ import re\r
+ import datetime\r
+ import traceback\r
+ \r
+ try:\r
+ import subprocess as sp\r
+ except:\r
+ sp = None\r
+\r
+ self.start = datetime.datetime.now()\r
+\r
+ cmdargs = [self.cmd]\r
+ cmdargs.extend(self.args)\r
+ \r
+ if self.echo:\r
+ if sp:\r
+ print( "\n%s : %s\n" % (self.__class__, sp.list2cmdline(cmdargs)) )\r
+ else:\r
+ print( "\n%s : %s\n" % (self.__class__, " ".join(cmdargs)) )\r
+\r
+ # intialize a few variables that may or may not be set later\r
+ process = None\r
+ tmpWrapper = None\r
+ stdout = None\r
+ stdin = None\r
+ parentenv = os.environ\r
+ parentcwd = os.getcwd()\r
+\r
+ try:\r
+ # Using subprocess\r
+ if sp:\r
+ if self.batchWrapper:\r
+ cmd = " ".join(cmdargs)\r
+ tmpWrapper = os.path.join(self.cwd, "process.bat")\r
+ writeText(cmd, tmpWrapper)\r
+ print( "%s : Running process through wrapper %s\n" % (self.__class__, tmpWrapper) )\r
+ process = sp.Popen([tmpWrapper], stdout=sp.PIPE, stderr=sp.STDOUT, \r
+ cwd=self.cwd, env=self.env)\r
+ else:\r
+ process = sp.Popen(cmdargs, stdout=sp.PIPE, stderr=sp.STDOUT, \r
+ cwd=self.cwd, env=self.env)\r
+\r
+ # using os.popen4\r
+ else:\r
+ if self.env:\r
+ os.environ = self.env\r
+ if self.cwd:\r
+ os.chdir( self.cwd )\r
+ \r
+ stdin, stdout = os.popen4( cmdargs, 'r')\r
+ except:\r
+ print( "Couldn't execute command : %s" % cmdargs[0] )\r
+ traceback.print_exc()\r
+\r
+ # Using subprocess\r
+ if sp:\r
+ if process != None:\r
+ #pid = process.pid\r
+ #log.logLine( "process id %s\n" % pid )\r
+\r
+ try:\r
+ # This is more proper python, and resolves some issues with a process ending before all\r
+ # of its output has been processed, but it also seems to stall when the read buffer\r
+ # is near or over it's limit. this happens relatively frequently with processes\r
+ # that generate lots of print statements.\r
+ #\r
+ for line in process.stdout:\r
+ self.logLine(line)\r
+ #\r
+ # So we go with the, um, uglier option below\r
+\r
+ # This is now used to ensure that the process has finished\r
+ line = ""\r
+ while line != None and process.poll() == None:\r
+ try:\r
+ line = process.stdout.readline()\r
+ except:\r
+ break\r
+ # 3.1\r
+ try:\r
+ self.logLine( str(line, encoding="utf-8") )\r
+ # 2.6\r
+ except:\r
+ self.logLine( line )\r
+ except:\r
+ self.logLine( "Logging error : %s" % sys.exc_info()[0] )\r
+\r
+ self.status = process.returncode\r
+ \r
+ if self.batchWrapper and tmpWrapper:\r
+ try:\r
+ os.remove(tmpWrapper)\r
+ except:\r
+ print( "Couldn't remove temp wrapper : %s" % tmpWrapper )\r
+ traceback.print_exc()\r
+\r
+ # Using os.popen4\r
+ else:\r
+ exitCode = -1\r
+ try:\r
+ #print( "reading stdout lines" )\r
+ stdoutLines = stdout.readlines()\r
+ exitCode = stdout.close()\r
+\r
+ stdout.close()\r
+ stdin.close()\r
+\r
+ if self.env:\r
+ os.environ = parentenv\r
+ if self.cwd:\r
+ os.chdir( parentcwd )\r
+ \r
+ if len( stdoutLines ) > 0:\r
+ for line in stdoutLines:\r
+ self.logLine(line)\r
+\r
+ if not exitCode:\r
+ exitCode = 0\r
+ except:\r
+ self.logLine( "Logging error : %s" % sys.exc_info()[0] )\r
+\r
+ self.status = exitCode\r
+ \r
+ self.end = datetime.datetime.now()\r
+ #execute\r
+# Process\r
+\r
+class ProcessList(Process):\r
+ "A list of processes with logged output"\r
+\r
+ def __init__(self, description, blocking=True, cwd=None, env=None):\r
+ Process.__init__(self, description, None, None, cwd, env)\r
+ "Initialize the standard class variables"\r
+ self.processes = []\r
+ self.blocking = blocking\r
+ # __init__\r
+\r
+ def generateReport(self, writeDict):\r
+ "Generate a log based on the success of the child processes"\r
+ if self.processes:\r
+ _status = True\r
+ indent = '\t'*(writeDict['indentationLevel']+1)\r
+ \r
+ self.log = []\r
+ \r
+ for child in self.processes:\r
+ if isinstance(child, ProcessList):\r
+ child.generateReport(writeDict)\r
+ \r
+ childResult = ""\r
+ key = child.description\r
+ value = child.status\r
+ if writeDict['format'] == 'xml':\r
+ childResult = ( "%s<result description=\"%s\">%s</result>" % (indent, key, value) )\r
+ else: # writeDict['format'] == 'txt':\r
+ childResult = ( "%s%40s : %s" % (indent, key, value) )\r
+ self.log.append( childResult )\r
+ \r
+ if child.status != 0:\r
+ _status = False\r
+ if not _status:\r
+ self.status = -1\r
+ else:\r
+ self.status = 0\r
+ else:\r
+ self.log = ["No child processes available to generate a report"]\r
+ self.status = -1\r
+\r
+ def writeLogHeader(self, writeDict):\r
+ self.writeKey(writeDict, 'processList', None, 'start' )\r
+ writeDict['indentationLevel'] += 1\r
+\r
+ self.writeKey(writeDict, 'description', self.description )\r
+ self.writeKey(writeDict, 'start', self.start )\r
+ self.writeKey(writeDict, 'end', self.end )\r
+ self.writeKey(writeDict, 'elapsed', self.getElapsedSeconds() )\r
+\r
+ self.generateReport(writeDict)\r
+\r
+ self.writeKey(writeDict, 'status', self.status )\r
+ # writeLogHeader\r
+\r
+ def writeLogFooter(self, writeDict):\r
+ writeDict['indentationLevel'] -= 1\r
+ self.writeKey(writeDict, 'processList', None, 'stop' )\r
+ # writeLogFooter\r
+\r
+ def writeLog(self, logHandle=sys.stdout, indentationLevel=0,format='xml'):\r
+ "Write logging information to the specified handle"\r
+ \r
+ writeDict = {}\r
+ writeDict['logHandle'] = logHandle\r
+ writeDict['indentationLevel'] = indentationLevel\r
+ writeDict['format'] = format\r
+ \r
+ if logHandle:\r
+ self.writeLogHeader(writeDict)\r
+ \r
+ if self.log:\r
+ self.writeKey(writeDict, 'output', None, 'start' )\r
+ for line in self.log:\r
+ logHandle.write( '%s%s\n' % ("", line) )\r
+ self.writeKey(writeDict, 'output', None, 'stop' )\r
+\r
+ if self.processes:\r
+ self.writeKey(writeDict, 'processes', None, 'start' )\r
+ for child in self.processes:\r
+ child.writeLog( logHandle, indentationLevel + 1, format )\r
+ self.writeKey(writeDict, 'processes', None, 'stop' )\r
+\r
+ self.writeLogFooter(writeDict)\r
+ # writeLog\r
+\r
+ def execute(self):\r
+ "Execute this list of processes"\r
+ import datetime\r
+\r
+ self.start = datetime.datetime.now()\r
+\r
+ self.status = 0\r
+ if self.processes:\r
+ for child in self.processes:\r
+ if child:\r
+ try:\r
+ child.execute()\r
+ except:\r
+ print( "%s : caught exception in child class %s" % (self.__class__, child.__class__) )\r
+ traceback.print_exc()\r
+ child.status = -1\r
+\r
+ if self.blocking and child.status != 0:\r
+ print( "%s : child class %s finished with an error" % (self.__class__, child.__class__) )\r
+ self.status = -1\r
+ break\r
+\r
+ self.end = datetime.datetime.now()\r
+ # execute\r
+# ProcessList\r
+\r
+def main():\r
+ import optparse\r
+\r
+ p = optparse.OptionParser(description='A process logging script',\r
+ prog='process',\r
+ version='process 0.1',\r
+ usage='%prog [options] [options for the logged process]')\r
+ p.add_option('--cmd', '-c', default=None)\r
+ p.add_option('--log', '-l', default=None)\r
+\r
+ options, arguments = p.parse_args()\r
+\r
+ #\r
+ # Get options\r
+ # \r
+ cmd = options.cmd\r
+ logFilename = options.log\r
+\r
+ try:\r
+ argsStart = sys.argv.index('--') + 1\r
+ args = sys.argv[argsStart:]\r
+ except:\r
+ argsStart = len(sys.argv)+1\r
+ args = []\r
+\r
+ if cmd == None:\r
+ print( "process: No command specified" )\r
+ \r
+ #\r
+ # Test regular logging\r
+ #\r
+ process = Process(description="a process",cmd=cmd, args=args)\r
+\r
+ #\r
+ # Test report generation and writing a log\r
+ #\r
+ processList = ProcessList("a process list")\r
+ processList.processes.append( process )\r
+ processList.echo = True\r
+ processList.execute()\r
+ \r
+ processList.writeLogToDisk(logFilename)\r
+# main\r
+\r
+if __name__ == '__main__':\r
+ main()\r