#!/bin/bash

################################################################################
################################################################################
# Copyright 2023-2025 by NI SP Software GmbH, All rights reserved.
# Copyright 1999-2023 by Nice, srl., All rights reserved.
#
# This software includes confidential and proprietary information
# of NI SP Software GmbH ("Confidential Information").
# You shall not disclose such Confidential Information
# and shall use it only in accordance with the terms of
# the license agreement you entered into with NI SP Software.
################################################################################
################################################################################
################################################################################


# -----------------------------------------------------------------------------
# function qstatxml()
#   retrieve job list xml from GE qstat -xml command
#
# input:
#   user  - user name (optional)
#   jobid - jobid (optional)
#   host  - target host (optional)
# output:
#   xml from qstat command, wrapped into <job-list>
# -----------------------------------------------------------------------------
ef_qstatxml () {

    # Check user
    if [ -z "${user}" -o "${user}" = "all" ]; then
        _user_params="-u \*"
    else
        _user_params=""
    fi

    # Check host
    if [ -n "${host}" ]; then
        _host_params="-s rsh -q \*@${host}"
    else
        _host_params=""
    fi

    echo "<grid:job-list type=\"sge\" cluster=\"$(ef_xml_escape_attribute -i "${SGE_CLUSTER_ID}")\" ${EF_XMLNS_grid}>"
    eval "${SGE_BINDIR}/qstat" ${_user_params} ${_host_params} -xml \
    | sed '/^ *<\/*job_info.*>/d;/^ *<\/*queue_info.*>/d;1d' \
    | awk '
        /<JB_submission_time>/ {
            gsub("<JB_submission_time>", "");
            gsub("</JB_submission_time>", "");
            split ($0, datetoken, "[-T]");
            split ($1, timetoken, "[T:]");

            # Translate months to numbers
            sub("01", "Jan", datetoken[2])
            sub("02", "Feb", datetoken[2])
            sub("03", "Mar", datetoken[2])
            sub("04", "Apr", datetoken[2])
            sub("05", "May", datetoken[2])
            sub("06", "Jun", datetoken[2])
            sub("07", "Jul", datetoken[2])
            sub("08", "Aug", datetoken[2])
            sub("09", "Sep", datetoken[2])
            sub("10", "Oct", datetoken[2])
            sub("11", "Nov", datetoken[2])
            sub("12", "Dec", datetoken[2])

            print "<grid:submission-time year=\"" datetoken[1] "\" month=\"" datetoken[2] "\" day=\"" datetoken[3] "\" hour=\"" timetoken[2] "\" minute=\"" timetoken[3] "\" />";
        }
        {
            printf "%s",$0;
        }
        '
    echo "</grid:job-list>"
}


# This function call qstat and create a sorted output in gridml
ef_qstat2xml () {
    # sort settings thru session variables
    if [ "x$sort_order" = "xascending" -a "x$_sortby" = "x$job_sortby" ]; then
        sort_order="descending"
    else
        sort_order="ascending"
    fi

    echo "<ef:session>"
    echo "  <ef:option id=\"sort_order\">$sort_order</ef:option>"
    echo "</ef:session>"
    echo "<ef:session>"
    echo "  <ef:option id=\"job_sortby\">$(ef_xml_escape_content -i "${_sortby}")</ef:option>"
    echo "</ef:session>"

    STATUSPARAM="-s prsz"
    [ "${SGE_DISABLE_QACCT}" = "true" ] && STATUSPARAM="-s rsh"

    if [ ! "${expandArray}" = "true" -a -z "${host}" ]; then
      COMPRESS_JOB_ARRAY="true"
      export COMPRESS_JOB_ARRAY
    fi

    QACCTUSERPARAM=""
    if [ -n "${QACCT_DAYS}" ]; then
      QACCTTIMEPARAM="-d ${QACCT_DAYS} ${QACCT_CUSTOM_PARAMS}"
    else
      QACCTTIMEPARAM="-d 1 ${QACCT_CUSTOM_PARAMS}"
    fi

    if [ "x$user" = "x" ]; then
        USERPARAM="-u *"
        QACCTUSERPARAM="-o "

    elif [ "x$user" = "xall" ]; then
        USERPARAM="-u *"
        QACCTUSERPARAM="-o "
    else
        USERPARAM="-u $user"
        QACCTUSERPARAM="-o $user"
    fi

    HOSTPARAM=""
    if [ -n "$host" ]; then
        HOSTPARAM="-q *@$host"
        STATUSPARAM="-s rsh"
    fi

    if [ "x${_jobid}" = "x" ]; then
        _qstat_tmp_err_file=$(ef_create_tempfile _qstat_tmp_err_file_$$)
        _qstat_tmp_out_file=$(ef_create_tempfile _qstat_tmp_out_file_$$)
        $("${SGE_BINDIR}/qstat" ${USERPARAM} ${HOSTPARAM} ${STATUSPARAM} $@ -xml 2>"${_qstat_tmp_err_file}" 1>"${_qstat_tmp_out_file}")
        _exit_code="$?"
        if [ "${_exit_code}" != "0" ]; then
            ef_error "$(cat "${_qstat_tmp_err_file}") - exit code (${_exit_code})" \
                "EnginFrame GE Plugin Error" \
                "${SGE_BINDIR}/qstat ${USERPARAM} ${HOSTPARAM} ${STATUSPARAM} $@ -xml"
            exit ${_exit_code}
        fi
        JOBDATA=$(cat "${_qstat_tmp_out_file}" | qstat_formatter 2>/dev/null)
        rm -f  "${_qstat_tmp_out_file}" "${_qstat_tmp_err_file}" 2>/dev/null
    else
        _jobid=`echo "${_jobid}" | sed ':a;N;$!ba;s/\n/,/g' | sed 's/\[[^\]]*\]//g'`
        _qstat_out=$("${SGE_BINDIR}/qstat" -u '*' ${STATUSPARAM} $@ -xml 2>&1)
        _exit_code="$?"
        if [ "${_exit_code}" != "0" ]; then
            ef_error "${_qstat_out} - exit code (${_exit_code})" \
                "EnginFrame GE Plugin Error" \
                "${SGE_BINDIR}/qstat -u '*' ${STATUSPARAM} $@ -xml"
            exit ${_exit_code}
        fi
        JOBDATA=$(echo "${_qstat_out}" | qstat_formatter ${_jobid} 2>/dev/null)
    fi

    # Debug
    #echo "          [DEBUG] --- JOBDATA:"
    #echo "${JOBDATA}" | sed 's/^/          [DEBUG] /g'

    # XML header
    [ "${COMPRESS_JOB_ARRAY}" = "true" ] && arrayMode='mode="array"' || arrayMode=""

    echo "<grid:job-list type=\"sge\" cluster=\"$(ef_xml_escape_attribute -i "${SGE_CLUSTER_ID}")\" ${arrayMode} ${EF_XMLNS_grid}>"

    # data output
    if [ -n "${JOBDATA}" ]; then
        ef_qstat_table | ef_qstat_sort | ef_qstat_xml
    fi

    # footer
    echo "</grid:job-list>"
}


# ef_qstat_table produce an output raw table. This one can be sorted easily
ef_qstat_table () {
    JOBLIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '{printf($1)"%"}'`
    JOBLIST_SEP=`echo ${JOBLIST} | tr "%" " "`
    JOBLIST_SEP_UNIQ=`echo ${JOBLIST} | awk 'BEGIN{ FS="%"} { for(i=1;i<=NF;i++) { if(seen[$i] != 1) { seen[$i]=1; printf "%s ",$i }}}'`
    TASKLIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '{printf($2)"%"}'`
    STATUSLIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '{printf($3)"%"}'`
    DATELIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '{printf($4)"%"}'`
    QUEUELIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '{split($6,QUEUE,"@"); printf(QUEUE[1])"%"}'`
    TIMELIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '{printf($5)"%"}'`
    EXECUTIONLIST=`echo -n ${JOBDATA} | tr ";" "\n" | ${EF_AWK} -F' ' '
        {
            hl="";
            # $6: queue@host,queue2@host2,...
            split($6,hostlist,",");
            for (i in hostlist) {
                split(hostlist[i], h, "@");
                hl=hl","h[2];
            }
            gsub(/^,/,"",hl);
            printf(hl"%");
        }'`

    (
    echo "---statuslist:    ${STATUSLIST}"
    echo "---joblist:       ${JOBLIST}"
    echo "---tasklist:      ${TASKLIST}"
    echo "---executionlist: ${EXECUTIONLIST}"
    echo "---datelist:      ${DATELIST}"
    echo "---queuelist:     ${QUEUELIST}"
    echo "---timelist:      ${TIMELIST}"


    # qstat -j only reports jobs that are pending or running, no exited jobs
    _last_jobid=""
    _last_jobinfo=""
    for i in ${JOBLIST_SEP} ; do
        if [ "$i" != "${_last_jobid}" ]; then
            # Cleanup qstat output and escape percent sign since it is used as format specifier in the printf statement inside the qstat2xml awk script
            _last_jobinfo=$(${SGE_BINDIR}/qstat -j $i 2>&1 | sed -n '1,/Total System Usage/p' | sed 's/%/%%/g' | grep -E -v "Total System Usage|queue instance")
            _last_jobid=$i
        fi
        echo "${_last_jobinfo}"
    done

    if [ ! "${SGE_DISABLE_QACCT}" = "true" -o -n "${_jobid}" ]; then
        printf "\n---qacct-zone\n"
        for i in ${JOBLIST_SEP_UNIQ} ; do
          ${SGE_BINDIR}/qacct ${QACCTUSERPARAM} ${QACCTTIMEPARAM} -j $i 2>/dev/null
        done
    fi

    # Try to retrieve pids in case of job details
    if [ -n "${_jobid}" ]; then
        PIDSLIST=$(2>/dev/null find "${SGE_ROOT}"/"${SGE_CELL}"/spool/*/active_jobs/${_jobid}* -name job_pid -exec printf " " \; -exec cat {} \;)
        echo "---pidslist:   ${PIDSLIST}"
    fi

    ) \
    | "${EF_AWK}" \
        -v COMPRESS_ARRAY="${COMPRESS_JOB_ARRAY}" \
        -v "statusmap=${EF_ROOT}/plugins/sge/conf/grid.job.status.mapping" \
        -F' ' \
        -f "${EF_ROOT}/plugins/ef/lib/awk/utils.awk" \
        -f "${EF_ROOT}/plugins/sge/bin/qstat2xml.table.awk"
}


# sort the table's records
ef_qstat_sort () {
    _cat="cat"
    if [ "x$sort_order" = "xdescending" ]; then
        _reverse="-r"
        _cat="tac"
    fi

    case $_sortby in
        "id")      sort $_reverse -t';' -k1 -n ;;
        "owner")   sort $_reverse -t';' -k13   ;;
        "account") sort $_reverse -t';' -k11   ;;
        "queue")   sort $_reverse -t';' -k10   ;;
        "time")    sort $_reverse -t';' -k6    ;;
        "status")  sort $_reverse -t';' -k12   ;;
        "host")    sort $_reverse -t';' -k5    ;;
        "name")    sort $_reverse -t';' -k9    ;;
        *)         $_cat                       ;;
    esac
}


# Create the job xml from a table row
ef_qstat_xml () {
    "${EF_AWK}" \
        -v GRID_TAG_SIZE_LIMIT="${GRID_TAG_SIZE_LIMIT}" \
        -F';' \
        -f "${EF_ROOT}/plugins/ef/lib/awk/utils.awk" \
        -f "${EF_ROOT}/plugins/sge/bin/qstat2xml.xml.awk"
}


#
# Takes the output of qstat and create a formatted output
# This function supports parallel jobs and create the following output:
# jobid status date time queue@host[,queue@host][,queue@host]...
#
qstat_formatter() {
    local _formatter_jobid="${1}"

    "${EF_AWK}" \
        -v jobid="${_formatter_jobid}" \
        -f "${EF_ROOT}/plugins/ef/lib/awk/utils.awk" \
        -f "${EF_ROOT}/plugins/sge/bin/qstat2xml.formatter.awk"
}

# Brief mode job listing
# Key point is to use qstat -j * -xml, ignoring finished jobs and qacct data
# Kindly developed with the help of Andrew Craven
ef_qstat2xml_brief() {
  declare -a USERPARAM
  USERPARAM+=("-u")
  [ -z "${user}" ] && USERPARAM+=('*') || USERPARAM+=(${user})

  echo "<grid:brief-job-list cluster=\"$(ef_xml_escape_attribute -i "${SGE_CLUSTER_ID}")\" user=\"${user}\" ${EF_XMLNS_grid}>"
  qstat "${USERPARAM[@]}" -j "*" -xml | sed '1d'

  # Add the missing pending job info by running a second qstat
  # Skip jobs in 'Eqw' since already accounted above.
  IFS=$'\n'
  for line in `qstat "${USERPARAM[@]}" -s p | grep -v Eqw | awk '\$9 ~/[[:digit:]]+/{print \$1, \$9}'`
  do
    unset IFS
    a=( $line )
    count=`echo ${a[1]} | awk -F ',' '{for(i=1;i<=NF;i++) if ($i ~/-/) { split($i,a,"-"); x += (a[2] - a[1]); } print (i-1)+x}'`
    echo "
    <grid:job id=\"$(ef_xml_escape_attribute -i "${a[0]}")\" is-array=\"true\" type=\"sge\" ${EF_XMLNS_grid}>
    <grid:array-status>
     <grid:counter ef=\"Pending\" grid=\"PEND\">$count</grid:counter>
    </grid:array-status>
    </grid:job>"
  done

  echo "</grid:brief-job-list >"
}

#
# vi: ts=4 sw=4 et syntax=sh :
#

