#!/usr/bin/env python
###############################################################################
# (c) Copyright 2013 CERN                                                     #
#                                                                             #
# This software is distributed under the terms of the GNU General Public      #
# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
#                                                                             #
# In applying this licence, CERN does not waive the privileges and immunities #
# granted to it by virtue of its status as an Intergovernmental Organization  #
# or submit itself to any jurisdiction.                                       #
###############################################################################
'''
Run a LHCbPR job

'''
__author__ = 'Ben Couturier <ben.couturier@cern.ch>'

# We first try to import from LbCommon, then revert to the old package (LbUtils)
# if needed
try:
    from LbCommon.Script import PlainScript as _PlainScript
except:
    from LbUtils.Script import PlainScript as _PlainScript

import sys
import os
from datetime import datetime
from LbPR.LbPRJobManager import JobManager
from LbNightlyTools import Configuration

def getProjectVersionFromConfig(config, project):
    ''' Look up the project version for SetupProject '''
    retval = None
    for proj in config[u'projects']:
        if proj['name'].lower() == project.lower():
            retval = proj['version']
            break
    return retval


def getSlotDateFromConfig(config):
    completed = config["completed"]
    return datetime.strptime(completed, '%Y-%m-%dT%H:%M:%S.%f')


class Script(_PlainScript):

    '''
    Script to create the commands to run a LHCbPR Job
    '''
    __usage__ = '%prog  <command> <project> <version> <platform>' \
                ' <options> <setup-options> <slot config>'
    __version__ = ''

    def defineOpts(self):
        '''Define options.'''
        from LbNightlyTools.Scripts.Common import addBasicOptions
        self.parser.add_option('-u', '--url', action='store',
                               help='URL for the LHCbPR REST service')
        self.parser.add_option('-c', '--check-ssl', action='store_true',
                               help='Check SSL certificate.')
        self.parser.add_option('-o', '--output', action='store',
                               help='output file name '
                                    '[default: runlhcbpr.sh]')
        addBasicOptions(self.parser)

    @staticmethod
    def interpretOptions(options, executable, app_name, app_version, platform):
        if not executable:
            return options["content"]

        return executable["content"].format(
            app_name=app_name,
            app_version=app_version,
            platform=platform,
            build="$(pwd)/../build",
            options=options["content"])

    @staticmethod
    def interpretSetup(setup, app_name, app_version):
        if not setup:
            return ''

        return setup["content"].format(app_name=app_name, app_version=app_version)

    @staticmethod
    def interpretSetupArgs(setupcontent):
        if setupcontent:
            return "--setup-name='{setup[description]}' --setup-content='{setup[content]}'".format(
                setup=setupcontent)
        return ""

    @staticmethod
    def interpretExecArgs(options):
        if not options:
            return ""
        return ("--exec-name='{ex[name]}' --exec-content='{ex[content]}'".
                format(ex=options))

    def main(self):
        '''
        Main function of the script.
        '''

        # Checking the arguments
        if len(self.args) != 6:
            self.parser.error('Please specify <project> <version> <platform> '
                              '<options> <setup-options> <slot config>')
            exit(1)

        application = self.args[0]
        version = self.args[1]
        platform = self.args[2]
        job_options = self.args[3]

        # Handler | Executable
        # Handler | Executable | Setup
        testenv = self.args[4].split("|")
        executuable_options = testenv[0]
        handlers = testenv[1]
        setup_options = None if len(testenv) < 3 else testenv[2]

        slot_config = self.args[5]

        # Parsing the slot config to extract relevant information
        self.log.info("Parsing config file %s" % slot_config)
        config = Configuration.load(slot_config)
        project_version = getProjectVersionFromConfig(config, application)
        slot_completed = getSlotDateFromConfig(config)
        # print("SASHA", slot_completed)
        self.log.info("Using %s %s", application, project_version)

        # Now create the interface with LHCbPR
        manager = JobManager(self.options.url, self.options.check_ssl)
        try:
            # Get the actual options based on description
            optionscontent = manager.getJobOptions(job_options)

            # Get the actual executable
            executablecontent = manager.getExecutableOptions(
                executuable_options)

            # Get the actual options based on description
            setupcontent = manager.getSetupOptions(setup_options)

        except Exception as exc:
            self.log.error("Could not get job description options "
                           "from LHCbPR service %s: %s", self.options.url,
                           str(exc))
            raise exc

        runfilename = "runlhcbpr.sh"
        if self.options.output != None:
            runfilename = self.options.output

        self.log.warning("Writing file: %s", runfilename)

        try:
            with open(runfilename, 'w') as runfile:
                # runfile.write("\n")
                # lblogin_cmd = ". LbLogin.sh -c %s" % platform
                setup_cmd = self.interpretSetup(
                    setup=setupcontent, app_name=application,
                    app_version=project_version)
                run_cmd = self.interpretOptions(
                    options=optionscontent,
                    executable=executablecontent,
                    app_name=application,
                    app_version=project_version,
                    platform=platform)
                print(run_cmd, optionscontent)
                setup_args = self.interpretSetupArgs(setupcontent)
                exec_args = self.interpretExecArgs(executablecontent)

                runfile.write('''
#!/usr/bin/env bash
#
# File generated by lbpr-get-command to run the LHCbPR Job
#

set -v

# Setting the CMTPROJECTPATH for the software installed locally
if [ -f build/setupSearchPath.sh ]; then
   . build/setupSearchPath.sh
fi
export User_release_area=$(pwd)/build
# Setting the environment and cleanup
OLDPATH=$PATH
{setup_cmd}
rm -f start.txt end.txt platform.txt
echo $CMTCONFIG > platform.txt

# Add valgrind from AFS to the PATH
if [ -f /afs/cern.ch/lhcb/group/rich/vol4/jonrob/scripts/new-valgrind.sh ]; then
 source /afs/cern.ch/lhcb/group/rich/vol4/jonrob/scripts/new-valgrind.sh
fi

# Adding jemalloc for the perf jobs
if [ -d /opt/dirac/lhcbpr/sw/jemalloc-3.6.0/lib ]; then
  export LD_LIBRARY_PATH=/opt/dirac/lhcbpr/sw/jemalloc-3.6.0/lib:$LD_LIBRARY_PATH
fi

# Get kerberos ticket
kinit -k -t /afs/cern.ch/user/l/lhcbpr/private/lhcbpr.keytab lhcbpr@CERN.CH

echo $LD_LIBRARY_PATH
# Now running the test itself
set -o pipefail
date +"%Y-%m-%d %T %z" > start.txt
time_start=`cat start.txt`
if [ -d output ]; then
  rm -rf output
fi
mkdir output
pushd $(pwd)
OUTPUT_PATH=output
cd $OUTPUT_PATH

# send the initial info to couchdb that the periodic test has started
log_cmd=`python - $1 $time_start << END
import sys
import os
import logging
from LbNightlyTools.Utils import Dashboard
logging.basicConfig(level=logging.DEBUG)
dash = Dashboard(credentials=None,
                 flavour='periodic')
build_id = sys.argv[2] + "_" + sys.argv[3] + "_" + sys.argv[4]
if 'BUILD_ID' in os.environ:
     build_id = os.environ.get('BUILD_ID')
doc_name = build_id + "." + sys.argv[1]
data_dict = dict()
if 'BUILD_URL' in os.environ:
    data_dict['build_url'] = os.environ.get('BUILD_URL') + '/console'
data_dict['app_name'] = '{app_name}'
data_dict['app_version'] = '{app_version}'
data_dict['opt_name'] = '{options[description]}'
data_dict['time_start'] = sys.argv[2] + "_" + sys.argv[3] + "_" + sys.argv[4]
data_dict['status'] = 'running'
data_dict['app_version_datetime'] = '{app_version_datetime}'
dash.update(doc_name, data_dict)
END`

# Set environment for throughput tests
if [[ '{handlers}' == *"Throughput"* ]] ; then
  source /cvmfs/projects.cern.ch/intelsw/psxe/linux/all-setup.sh
  if [ -d ../lhcb-benchmark-scripts ] ; then
    git --git-dir=../lhcb-benchmark-scripts/.git fetch --all
    git --git-dir=../lhcb-benchmark-scripts/.git reset --hard origin/master
  else
    git clone --quiet ssh://git@gitlab.cern.ch:7999/lhcb-core/lhcb-benchmark-scripts.git ../lhcb-benchmark-scripts
  fi
  ln -sfv ../lhcb-benchmark-scripts/* .
fi

if [ -z "$LHCBPR_MOCK_OUTPUT_PATH" ]
then
    {run_cmd} 2>&1 | tee run.log
else
    OUTPUT_PATH="$LHCBPR_MOCK_OUTPUT_PATH"
fi
RETCODE=$?
popd
PATH=$OLDPATH

date +"%Y-%m-%d %T %z" > end.txt
COLLECT_LOG=$OUTPUT_PATH/collect.log
echo "==> Return code: $RETCODE" | tee $COLLECT_LOG
# Gathering the results with LHCbPR

if [ "$RETCODE" = "0" ] ; then
    echo "Run OK now gathering the results" | tee -a $COLLECT_LOG
else
    echo "Non 0 return code - Will NOT gather the results" | tee -a $COLLECT_LOG
fi

# FIXME, temporal workaround for MiniBrunel-Callgrind test
if [ '{options[description]}' == 'MiniBrunel-Callgrind' ]
then
valgrindroot=/cvmfs/lhcbdev.cern.ch/tools/valgrind/3.12.0/x86_64-centos7
export PATH=$valgrindroot/bin:$PATH
export LD_LIBRARY_PATH=$valgrindroot/lib/valgrind:$LD_LIBRARY_PATH
for i in $OUTPUT_PATH/callgrind.out.*.1; do
if [ -f "$i" ]; then
callgrind_log=`ls -tr $OUTPUT_PATH/callgrind.out.*.1 | tail -1`
callgrind_annotate --inclusive=yes $callgrind_log > $callgrind_log.anno
fi
done
fi

time_end=`cat end.txt`
hostname=`hostname`
cpu_info=`lscpu | grep "^Model name:" | cut -d: -f2 | sed -e 's/^[[:space:]]*//'`
mem_info=`cat /proc/meminfo | grep MemTotal`

lbpr-collect -r $OUTPUT_PATH -s "`cat start.txt`" -e "`cat end.txt`" -p `hostname` -u "$cpu_info" -m "$mem_info" -c `cat platform.txt` --opt-content='{options[content]}' {setup_args} {exec_args} --opt-name='{options[description]}' -l '{handlers}' --app-name='{app_name}' --app-version='{app_version}'  --app-version-datetime="{app_version_datetime}" -i $1 -t $RETCODE -a | tee -a $COLLECT_LOG

# send the final info to couchdb that the periodic test has ended
log_cmd=`python - $1 $time_start $time_end $RETCODE $OUTPUT_PATH $CMTCONFIG $hostname "$cpu_info" "$mem_info"<< END
import sys
import os
import logging
import subprocess
from LbNightlyTools.Utils import Dashboard
logging.basicConfig(level=logging.DEBUG)
dash = Dashboard(credentials=None,
                 flavour='periodic')
build_id = sys.argv[2] + "_" + sys.argv[3] + "_" + sys.argv[4]
if 'BUILD_ID' in os.environ:
     build_id = os.environ.get('BUILD_ID')
doc_name = build_id + "." + sys.argv[1]
data_dict = dict()
data_dict['time_end'] = sys.argv[5] + "_" + sys.argv[6] + "_" + sys.argv[7]
data_dict['status'] = sys.argv[8]
logsDir = "logs/" + '{app_name}' + "/" + '{options[description]}' + "/" + '{app_version}' + "_" + sys.argv[2] + "_" + sys.argv[3] + "_" + sys.argv[4]
logName = os.path.join(sys.argv[9],'run.log')
eos_path = os.path.join(os.environ['LHCBPR_EOS_LOGS'], '')
targetDir = 'root://eoslhcb.cern.ch/' + eos_path + logsDir + '/run.log'
logCollectName = os.path.join(sys.argv[9],'collect.log')
targetCollectDir = 'root://eoslhcb.cern.ch/' + eos_path + logsDir + '/collect.log'
try:
    subprocess.call(['xrdcp', logName, targetDir])
    subprocess.call(['xrdcp', logCollectName, targetCollectDir])
except Exception, ex:
    logging.warning('Error copying log to eos: %s', ex)
data_dict['run_log'] = "https://eoslhcbhttp.cern.ch/" + eos_path + logsDir + "/run.log"
data_dict['collect_log'] = "https://eoslhcbhttp.cern.ch/" + eos_path + logsDir + "/collect.log"
cmtconfig_dict = dict()
host_dict = dict()
cmtconfig_dict['platform'] = sys.argv[10]
host_dict['hostname'] =  sys.argv[11]
host_dict['cpu_info'] = sys.argv[12]
host_dict['memoryinfo'] = sys.argv[13]
data_dict['CMTCONFIG'] = cmtconfig_dict
data_dict['HOST'] = host_dict
dash.update(doc_name, data_dict)
END`

exit $RETCODE
                '''.format(setup_cmd=setup_cmd,
                           run_cmd=run_cmd,
                           options=optionscontent,
                           setup_args=setup_args,
                           exec_args=exec_args,
                           handlers=handlers,
                           app_name=application,
                           app_version=version,
                           app_version_datetime
                           = slot_completed.strftime("%Y-%m-%d %H:%M:%S -0400")))
        except Exception as exc:
            self.log.error("Error generating runfile: %s",
                           str(exc))
            raise exc

# __main__
sys.exit(Script().run())
