#!/usr/bin/env python # # -*- mode:Python -*- # # This file is part of the Coriolis Software. # Copyright (c) UPMC 2015-2015, All Rights Reserved # # +-----------------------------------------------------------------+ # | C O R I O L I S | # | C o r i o l i s / C h a m s I n s t a l l e r | # | | # | Authors : Jean-Paul Chaput | # | E-mail : Jean-Paul.Chaput@asim.lip6.fr | # | =============================================================== | # | Python : "./socInstaller.py" | # +-----------------------------------------------------------------+ # # WARNING: # This script has been designed only for internal use in the # LIP6/SoC department. If you want to use it you will need to # change the hardwired configuration. showTrace = True try: import sys import os.path import shutil import optparse import time import traceback import distutils.sysconfig import subprocess import socket import re import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication except ImportError, e: module = str(e).split()[-1] class ErrorMessage ( Exception ): def __init__ ( self, code, *arguments ): self._code = code self._errors = [ 'Malformed call to ErrorMessage()', '%s' % str(arguments) ] text = None if len(arguments) == 1: if isinstance(arguments[0],Exception): text = str(arguments[0]).split('\n') else: self._errors = arguments[0] elif len(arguments) > 1: text = list(arguments) if text: self._errors = [] while len(text[0]) == 0: del text[0] lstrip = 0 if text[0].startswith('[ERROR]'): lstrip = 8 for line in text: if line[0:lstrip ] == ' '*lstrip or \ line[0:lstrip-1] == '[ERROR]': self._errors += [ line[lstrip:] ] else: self._errors += [ line.lstrip() ] return def __str__ ( self ): if not isinstance(self._errors,list): return "[ERROR] %s" % self._errors formatted = "\n" for i in range(len(self._errors)): if i == 0: formatted += "[ERROR] %s" % self._errors[i] else: formatted += " %s" % self._errors[i] if i+1 < len(self._errors): formatted += "\n" return formatted def addMessage ( self, message ): if not isinstance(self._errors,list): self._errors = [ self._errors ] if isinstance(message,list): for line in message: self._errors += [ line ] else: self._errors += [ message ] return def terminate ( self ): print self sys.exit(self._code) @property def code ( self ): return self._code class BadBinary ( ErrorMessage ): def __init__ ( self, binary ): ErrorMessage.__init__( self, 1, "Binary not found: <%s>." % binary ) return class BadReturnCode ( ErrorMessage ): def __init__ ( self, status ): ErrorMessage.__init__( self, 1, "Command returned status:%d." % status ) return class Command ( object ): def __init__ ( self, arguments, fdLog=None ): self.arguments = arguments self.fdLog = fdLog if self.fdLog != None and not isinstance(self.fdLog,file): print '[WARNING] Command.__init__(): is neither None or a file.' return def execute ( self ): sys.stdout.flush() sys.stderr.flush() try: child = subprocess.Popen( self.arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) while True: line = child.stdout.readline() if not line: break print line[:-1] sys.stdout.flush() if isinstance(self.fdLog,file): self.fdLog.write( line ) self.fdLog.flush() except OSError, e: raise BadBinary( self.arguments[0] ) (pid,status) = os.waitpid( child.pid, 0 ) status >>= 8 if status != 0: raise BadReturnCode( status ) return class GitRepository ( object ): @staticmethod def getLocalRepository ( gitRepository ): localRepo = gitRepository.split( '/' )[-1] if localRepo.endswith('.git'): localRepo = localRepo[:-4] return localRepo def __init__ ( self, url, cloneDir ): self.url = url self.cloneDir = cloneDir self.localRepo = GitRepository.getLocalRepository( url ) return @property def localRepoDir ( self ): return self.cloneDir+'/'+self.localRepo def removeLocalRepo ( self ): if os.path.isdir(self.localRepoDir): print 'Removing Git local repository: <%s>' % self.localRepoDir shutil.rmtree( self.localRepoDir ) return def clone ( self ): if not os.path.isdir(self.cloneDir): os.makedirs( self.cloneDir ) if not os.path.isdir(self.localRepoDir): os.chdir( self.cloneDir ) Command( [ 'git', 'clone', self.url ] ).execute() else: os.chdir( self.localRepoDir ) Command( [ 'git', 'pull' ] ).execute() return def checkout ( self, branch ): os.chdir( self.localRepoDir ) Command( [ 'git', 'checkout', branch ] ).execute() return def openLog ( logDir, stem ): if not os.path.isdir(logDir): os.makedirs( logDir ) index = 0 timeTag = time.strftime( "%Y.%m.%d" ) while True: logFile = os.path.join(logDir,"%s-%s-%02d.log" % (stem,timeTag,index)) if not os.path.isfile(logFile): print "Report log: <%s>" % logFile break index += 1 fd = open( logFile, "w" ) return (fd,logFile) def sendReport ( state, buildLog, benchsLog, nightlyBuild ): sender = 'Jean-Paul.Chaput@soc.lip6.fr' receiver = 'Jean-Paul.Chaput@lip6.fr' date = time.strftime( "%A %d %B %Y" ) stateText = 'FAILED' buildText = 'SoC installation' if state: stateText = 'SUCCESS' if nightlyBuild: buildText = 'Nightly build' message = MIMEMultipart() message['Subject'] = '[%s] Coriolis & Chams %s %s' % (stateText,buildText,date) message['From' ] = sender message['To' ] = receiver mainText = '\n' mainText += 'Salut le Crevard,\n' mainText += '\n' if nightlyBuild: mainText += 'This is the nightly build report of Coriolis & Chams.\n' else: mainText += 'SoC installer report of Coriolis & Chams.\n' mainText += '%s\n' % date mainText += '\n' if state: mainText += 'Build was SUCCESSFUL\n' else: mainText += 'Build has FAILED, please have a look to the attached log file.\n' mainText += '\n' mainText += 'Complete log file can be found here:\n' mainText += ' <%s>\n' % buildLog if benchsLog: mainText += ' <%s>\n' % benchsLog mainText += '\n' message.attach( MIMEText(mainText) ) fd = open( buildLog, 'rb' ) fd.seek( -1024*100, os.SEEK_END ) tailLines = '' for line in fd.readlines()[1:]: tailLines += line message.attach( MIMEApplication(tailLines) ) fd.close() if benchsLog: fd = open( benchsLog, 'rb' ) fd.seek( -1024*100, os.SEEK_END ) tailLines = '' for line in fd.readlines()[1:]: tailLines += line message.attach( MIMEApplication(tailLines) ) fd.close() session = smtplib.SMTP( 'localhost' ) session.sendmail( sender, receiver, message.as_string() ) session.quit() return def detectRunningHost (): runningHost = 'unknown' hostname = socket.gethostname() hostAddr = socket.gethostbyname(hostname) if hostname == 'lepka' and hostAddr == '127.0.0.1': print 'Running on , watchout mode enabled.' runningHost = 'lepka' else: runningHost = hostname.split('.')[0] return runningHost # ------------------------------------------------------------------- # Main Part. parser = optparse.OptionParser () parser.add_option ( "--debug" , action="store_true" , dest="debug" , help="Build a aka (-g) version." ) parser.add_option ( "--no-git" , action="store_true" , dest="noGit" , help="Do not pull/update Git repositories before building." ) parser.add_option ( "--no-report" , action="store_true" , dest="noReport" , help="Do not send a final report." ) parser.add_option ( "--nightly" , action="store_true" , dest="nightly" , help="Perform a nighly build." ) parser.add_option ( "--benchs" , action="store_true" , dest="benchs" , help="Run the sanity benchs." ) parser.add_option ( "--rm-build" , action="store_true" , dest="rmBuild" , help="Remove the build/install directories." ) parser.add_option ( "--rm-source" , action="store_true" , dest="rmSource" , help="Remove the Git source repositories." ) parser.add_option ( "--rm-all" , action="store_true" , dest="rmAll" , help="Remove everything (source+build+install)." ) parser.add_option ( "--root" , action="store" , type="string", dest="rootDir" , help="The root directory (default: <~/coriolis-2.x/>)." ) (options, args) = parser.parse_args () nightlyBuild = False fdBuildLog = None fdBenchsLog = None buildLogFile = None benchsLogFile = None try: rmSource = False rmBuild = False doGit = True doBench = False doSendReport = True debugArg = '' runningHost = detectRunningHost() targetSL6 = 'rock' targetSL6_64 = 'bip' targetSL7_64 = None if options.debug: debugArg = '--debug' if options.nightly: nightlyBuild = True if options.noGit: doGit = False if options.benchs: doBenchs = True if options.noReport: doSendReport = False if options.rmSource or options.rmAll: rmSource = True if options.rmBuild or options.rmAll: rmBuild = True if runningHost == 'lepka': print 'Never touch the Git tree when running on .' doGit = False rmSource = False rmBuild = False targetSL7_64 = 'lepka' targetSL6 = None targetSL6_64 = None if nightlyBuild: targetSL6 = None allianceCheckRepo = 'https://www-soc.lip6.fr/git/alliance-check-toolkit.git' coriolisRepo = 'https://www-soc.lip6.fr/git/coriolis.git' chamsRepo = 'file:///users/outil/chams/chams.git' homeDir = os.environ['HOME'] rootDir = homeDir + '/coriolis-2.x' if nightlyBuild: rootDir = homeDir + '/nightly/coriolis-2.x' srcDir = rootDir + '/src' logDir = srcDir + '/logs' gitCoriolis = GitRepository( coriolisRepo , srcDir ) gitChams = GitRepository( chamsRepo , srcDir ) gitAllianceCheck = GitRepository( allianceCheckRepo, srcDir ) if doGit: if rmSource: gitCoriolis.removeLocalRepo() gitCoriolis.clone () gitCoriolis.checkout( 'devel' ) if rmSource: gitChams.removeLocalRepo() gitChams.clone () gitChams.checkout( 'devel' ) if rmSource: AllianceCheck.removeLocalRepo() AllianceCheck.clone () if rmBuild: for entry in os.listdir(rootDir): if entry.startswith('Linux.'): buildDir = rootDir+'/'+entry print 'Removing OS build directory: <%s>' % buildDir shutil.rmtree( buildDir ) ccbBin = gitCoriolis.localRepoDir+'/bootstrap/ccb.py' if not os.path.isfile( ccbBin ): raise ErrorMessage( 1, [ 'Cannot find , should be here:' , ' <%s>' % ccbBin ] ) fdBuildLog,buildLogFile = openLog( logDir, 'build' ) if doBenchs: fdBenchsLog,benchsLogFile = openLog( logDir, 'benchs' ) buildCommand = '%s --root=%s --project=coriolis --project=chams --make="-j%%d install" %%s' \ % (ccbBin,rootDir) benchsCommand = 'cd %s/benchs && ./bin/go.sh clean && ./bin/go.sh lvx' \ % (gitAllianceCheck.localRepoDir) if targetSL7_64: commands = [ ( targetSL7_64, buildCommand % (3,debugArg) , fdBuildLog ) , ( targetSL7_64, buildCommand % (1,debugArg+' --doc'), fdBuildLog ) ] if doBenchs: commands += [ ( targetSL7_64, benchsCommand, fdBenchsLog ) ] if targetSL6_64: commands = [ ( targetSL6_64, buildCommand % (6,debugArg+' --devtoolset-2') , fdBuildLog ) , ( targetSL6_64, buildCommand % (1,debugArg+' --devtoolset-2 --doc'), fdBuildLog ) ] if doBenchs: commands += [ ( targetSL6_64, benchsCommand, fdBenchsLog ) ] if targetSL6: commands = [ ( targetSL6, buildCommand % (2,debugArg+' --devtoolset-2') , fdBuildLog ) , ( targetSL6, buildCommand % (1,debugArg+' --devtoolset-2 --doc'), fdBuildLog ) ] if doBenchs: commands += [ ( targetSL6, benchsCommand, fdBenchsLog ) ] for host,command,fd in commands: Command( [ 'ssh', host, command ], fd ).execute() fdBuildLog.close() if doBenchs: fdBenchsLog.close() if doSendReport: sendReport( True, buildLogFile, benchsLogFile, nightlyBuild ) except ErrorMessage, e: print e if fdBuildLog: fdBuildLog.close() if fdBenchsLog: fdBenchsLog.close() if showTrace: print '\nPython stack trace:' traceback.print_tb( sys.exc_info()[2] ) sendReport( False, buildLogFile, benchsLogFile, nightlyBuild ) sys.exit( e.code ) sys.exit( 0 )