Newer
Older
TB_Chris / Kepler / GangaPlugin / Lib / KeplerApp.py
## Note that the special string AppName will be replaced upon initialisation
## in all cases with the relavent app name (DaVinci, Gauss etc...)
import os, tempfile, pprint, sys
from GangaGaudi.Lib.Applications.Gaudi import Gaudi
from GangaGaudi.Lib.Applications.GaudiUtils import fillPackedSandbox
from GangaLHCb.Lib.Applications.AppsBaseUtils import available_apps, guess_version, available_packs
from GangaLHCb.Lib.Applications.AppsBaseUtils import backend_handlers, activeSummaryItems
from Ganga.GPIDev.Lib.File.FileBuffer import FileBuffer
from Ganga.GPIDev.Base.Proxy import GPIProxyObjectFactory
from Ganga.GPIDev.Schema import *
from Ganga.Utility.Shell import Shell
from GangaLHCb.Lib.Applications.PythonOptionsParser import PythonOptionsParser
from Ganga.GPIDev.Adapters.StandardJobConfig import StandardJobConfig
from Ganga.Utility.Config import getConfig
from Ganga.Utility.files import expandfilename
from Ganga.Utility.execute import execute
import Ganga.Utility.logging
import GangaLHCb.Lib.Applications.CMTscript
import pickle
import subprocess
from GangaLHCb.Lib.Applications import XMLPostProcessor
from Ganga.Core.exceptions import ApplicationConfigurationError

logger = Ganga.Utility.logging.getLogger()

class Kepler(Gaudi):
    _name = 'Kepler'
    _category = 'applications'
    #__doc__ = GaudiDocString('AppName')
    _schema = Gaudi._schema.inherit_copy()
    docstr = 'The package the application belongs to (e.g. "Sim", "Phys")'
    _schema.datadict['package'] = SimpleItem(defvalue=None,
                                             typelist=['str','type(None)'],
                                             doc=docstr)
    docstr = 'The package where your top level requirements file is read '  \
             'from. Can be written either as a path '  \
             '\"Tutorial/Analysis/v6r0\" or in a CMT style notation '  \
             '\"Analysis v6r0 Tutorial\"'
    _schema.datadict['masterpackage'] = SimpleItem(defvalue=None,
                                                   typelist=['str','type(None)'],
                                                   doc=docstr)
    _schema.datadict['platform'] = SimpleItem( defvalue="x86_64-slc6-gcc48-opt" )
    docstr = 'Extra options to be passed onto the SetupProject command '\
             'used for configuring the environment. As an example '\
             'setting it to \'--dev\' will give access to the DEV area. '\
             'For full documentation of the available options see '\
             'https://twiki.cern.ch/twiki/bin/view/LHCb/SetupProject'
    _schema.datadict['setupProjectOptions'] = SimpleItem(defvalue='',
                                                         typelist=['str','type(None)'],
                                                         doc=docstr)    
    _exportmethods = Gaudi._exportmethods[:]
    _exportmethods += ['readInputData']
    
    def _get_default_version(self, gaudi_app):
        return guess_version(gaudi_app)
    
               
    def _auto__init__(self):
        self.appname='Kepler'
      #  super(self.appname, self)._auto__init__()

    def postprocess(self):
        XMLPostProcessor.postprocess(self,logger)

    def readInputData(self,optsfiles,extraopts=False):
        def dummyfile():
            temp_fd,temp_filename=tempfile.mkstemp(text=True,suffix='.py')
            os.write(temp_fd,"Dummy file to keep the Optionsparser happy")
            os.close(temp_fd)
            return temp_filename

        if type(optsfiles)!=type([]): optsfiles=[optsfiles]
        
        if len(optsfiles)==0: optsfiles.append(dummyfile())
        
        if extraopts: extraopts=self.extraopts
        
        else: extraopts=""
            
       # parser = check_inputs(optsfiles, extraopts, self.env) 
        try:
            parser = PythonOptionsParser(optsfiles,extraopts,self.getenv(False))
        except Exception, e:
            msg = 'Unable to parse the job options. Please check options ' \
                  'files and extraopts.'
            raise ApplicationConfigurationError(None,msg)

        return GPIProxyObjectFactory(parser.get_input_data())

    def getpack(self, options=''):
        """Performs a getpack on the package given within the environment
           of the application. The unix exit code is returned
        """
        command = 'getpack ' + options + '\n'
        if options == '':
            command = 'getpack -i'
        return CMTscript.CMTscript(self,command)
        
    def make(self, argument=None):
        """Performs a CMT make on the application. The unix exit code is 
           returned. Any arguments given are passed onto CMT as in
           dv.make('clean').
        """
        config = Ganga.Utility.Config.getConfig('GAUDI')
        command = config['make_cmd']
        if argument:
            command+=' '+argument
        return CMTscript.CMTscript(self,command)

    def cmt(self, command):
        """Execute a cmt command in the cmt user area pointed to by the
        application. Will execute the command "cmt <command>" after the
        proper configuration. Do not include the word "cmt" yourself. The 
        unix exit code is returned."""
        command = '###CMT### ' + command
        return CMTscript.CMTscript(self,command)

    def _getshell(self):
        opts = ''
        if self.setupProjectOptions: opts = self.setupProjectOptions

        fd = tempfile.NamedTemporaryFile()
        script = '#!/bin/sh\n'
        if self.user_release_area:
            script += 'User_release_area=%s; export User_release_area\n' % \
                      expandfilename(self.user_release_area)
        if self.platform:    
            script += '. `which LbLogin.sh` -c %s\n' % self.platform
        useflag = ''
        cmd = '. SetupProject.sh %s %s %s %s' % (useflag, opts, self.appname, self.version) 
        script += '%s \n' % cmd
        fd.write(script)
        fd.flush()
        logger.debug(script)

        self.shell = Shell(setup=fd.name)
        if (not self.shell): raise ApplicationConfigurationError(None,'Shell not created.')
        
        logger.debug(pprint.pformat(self.shell.env))
        
        fd.close()
        app_ok = False
        ver_ok = False
        for var in self.shell.env:
            if var.find(self.appname) >= 0: app_ok = True
            if self.shell.env[var].find(self.version) >= 0: ver_ok = True
        if not app_ok or not ver_ok:
            msg = 'Command "%s" failed to properly setup environment.' % cmd
            logger.error(msg)
            raise ApplicationConfigurationError(None,msg)

        import copy
        self.env = copy.deepcopy( self.shell.env )

        return self.shell.env


    def _get_parser(self):
        optsfiles = [fileitem.name for fileitem in self.optsfile]
        # add on XML summary

        extraopts = ''
        if self.extraopts:
            extraopts += self.extraopts
            
        try:
            parser = PythonOptionsParser(optsfiles,extraopts,self.getenv(False))
        except ApplicationConfigurationError, e:
            # fix this when preparing not attached to job
            
            msg2=''
            try:
                debug_dir = self.getJobObject().getDebugWorkspace().getPath()
                msg2+='You can also view this from within ganga '\
                       'by doing job.peek(\'../debug/gaudirun.<whatever>\').'
            except:
                debug_dir = tempfile.mkdtemp()
                    
            messages = e.message.split('###SPLIT###')
            if len(messages) is 2:
                stdout = open(debug_dir + '/gaudirun.stdout','w')
                stderr = open(debug_dir + '/gaudirun.stderr','w')
                stdout.write(messages[0])
                stderr.write(messages[1])
                stdout.close()
                stderr.close()
                msg = 'Unable to parse job options! Please check options ' \
                      'files and extraopts. The output and error streams from gaudirun.py can be ' \
                      'found in %s and %s respectively . ' % (stdout.name, stderr.name)
            else:
                f = open(debug_dir + '/gaudirun.out','w')
                f.write(e.message)
                f.close()
                msg = 'Unable to parse job options! Please check options ' \
                      'files and extraopts. The output from gaudirun.py can be ' \
                      'found in %s . ' % f.name
            msg+=msg2
            # logger.error(msg)
            raise ApplicationConfigurationError(None,msg)
        return parser


    def _parse_options(self):
        try:
            parser = self._get_parser()
        except ApplicationConfigurationError, e:
            raise e

        share_dir = os.path.join(expandfilename(getConfig('Configuration')['gangadir']),
                                 'shared',
                                 getConfig('Configuration')['user'],
                                 self.is_prepared.name)
        fillPackedSandbox([FileBuffer('options.pkl',parser.opts_pkl_str)],
                          os.path.join(share_dir,
                                       'inputsandbox',
                                       '_input_sandbox_%s.tar' % self.is_prepared.name))
        inputdata = parser.get_input_data()
        if len(inputdata.files) > 0:
            logger.warning('Found inputdataset defined in optsfile, '\
                           'this will get pickled up and stored in the '\
                           'prepared state. Any change to the options/data will '\
                           'therefore require an unprepare first.')
            logger.warning('NOTE: the prefered way of working '\
                           'is to define inputdata in the job.inputdata field. ')
            logger.warning('Data defined in job.inputdata will superseed optsfile data!')
            logger.warning('Inputdata can be transfered from optsfiles to the job.inputdata field '\
                           'using job.inputdata = job.application.readInputData(optsfiles)')
            share_path = os.path.join(share_dir,'inputdata')
            if not os.path.isdir(share_path): os.makedirs(share_path)
            f=open(os.path.join(share_path,'options_data.pkl'),'w+b')
            pickle.dump(inputdata, f)
            f.close()

        share_path = os.path.join(share_dir,'output')
        if not os.path.isdir(share_path): os.makedirs(share_path)
        f=open(os.path.join(share_path,'options_parser.pkl'),'w+b')
        pickle.dump(parser, f)
        f.close()

from Ganga.GPIDev.Adapters.ApplicationRuntimeHandlers import allHandlers
for (backend, handler) in backend_handlers().iteritems():
    allHandlers.add('Kepler', backend, handler)