func parseXML(xmlOutput []byte) (ijs InternalJobStatus, err error) {
	err2 := xml.Unmarshal(xmlOutput, &ijs)
	if err2 != nil {
		return ijs, fmt.Errorf("XML unmarshall error")
	}
	return ijs, nil
}
parseXML parses the qstat -j -xml output and fills
the InternalJobStatus struct
package geparser
import (
	"encoding/xml"
	"fmt"
	"github.com/dgruber/drmaa"
	"log"
	"os/exec"
	"strconv"
	"time"
)
var cachedJobStatus = map[string]jobStatusCache{}
func getValidJobStatusFromCache(jobID string) (ijs InternalJobStatus, found bool) {
	if cachedJobStatus == nil {
		return ijs, false
	}
	if cjs, found := cachedJobStatus[jobID]; found {
		lastUpdate := time.Since(cjs.lastUpdate)
		if lastUpdate.Seconds() <= 5 {
			return cjs.jobStatus, true
		}
	}
	return ijs, false
}
func Unix(value, unused int64) time.Time {
	if value > 631152000000 {
		return time.Unix(int64(value/1000), 0)
	}
	return time.Unix(value, 0)
}
type JBEnvElem struct {
	VAvariable string `xml:"VA_variable"`
	VAvalue string `xml:"VA_value"`
	VAaccessSpecifier int `xml:"VA_access_specifier"`
}
type JBEnvList struct {
	JBEnvElement []JBEnvElem `xml:"element"`
}
type JBJobArgsElement struct {
	STname string `xml:"ST_name"`
	STpos int `xml:"ST_pos"`
	STaccessSpecifier int `xml:"ST_access_specifier"`
}
type JBJobArgs struct {
	JBjobArgs []JBJobArgsElement `xml:"element"`
}
type JATScaledElements struct {
	UAname string `xml:"UA_name"`
	UAvalue string `xml:"UA_value"`
}
type JATScaledUsageList struct {
	JATscaled82 []JATScaledElements `xml:"Events"`
	JATscaled []JATScaledElements `xml:"scaled"`
}
type JGBinding struct {
	JGbindingElement BNElement `xml:"binding"`
}
type GDILElement struct {
	JGqname string `xml:"JG_qname"`
	JGqversion string `xml:"JG_qversion"`
	JGqhostname string `xml:"JG_qhostname"`
	JGslots int `xml:"JG_slots"`
	JGqueue string `xml:"JG_queue"`
	JGtagslavejob int `xml:"JG_tag_slave_job"`
	JGticket float64 `xml:"JG_ticket"`
	JGoticket float64 `xml:"JG_oticket"`
	JGfticket float64 `xml:"JG_fticket"`
	JGsticket float64 `xml:"JG_sticket"`
	JGjcoticket float64 `xml:"JG_jcoticket"`
	JGjcfticket float64 `xml:"JG_jcfticket"`
	JGbinding JGBinding `xml:"JG_binding"`
}
type JATGDIL struct {
	GdilElement []GDILElement `xml:"element"`
}
type JBJaSublist struct {
	JATstatus int `xml:"JAT_status"`
	JATtasknumber int `xml:"JAT_task_number"`
	JATscaledusagelist JATScaledUsageList `xml:"JAT_scaled_usage_list"`
	JATstarttime int64 `xml:"JAT_start_time"`
	JATgranteddestinidentifierlist JATGDIL `xml:"JAT_granted_destin_identifier_list"`
}
type JBJaTasks struct {
	JaTaskSublist82 []JBJaSublist `xml:"element"`
	JaTaskSublist []JBJaSublist `xml:"ulong_sublist"`
}
type TaskidRange struct {
	RNmin int `xml:"RN_min"`
	RNmax int `xml:"RN_max"`
	RNstep int `xml:"RN_step"`
}
type JBJAStructure struct {
	TaskIDrange TaskidRange `xml:"task_id_range"`
}
type BNElement struct {
	BNstrategy string `xml:"BN_strategy"`
	BNtype int `xml:"BN_type"`
	BNparameterN int `xml:"BN_parameter_n"`
	BNparameterSocket int `xml:"BN_parameter_socket"`
	BNparameterCoreOffset int `xml:"BN_parameter_core_offset"`
	BNparameterStridingStepSize int `xml:"BN_parameter_striding_step_size"`
	BNparameterExplicit string `xml:"BN_parameter_explicit"`
	BNparameterNlocal int `xml:"BN_parameter_nlocal"`
}
type JBBinding struct {
	BindingElement BNElement `xml:"element"`
}
type MRElement struct {
	User string `xml:"MR_user"`
	Host string `xml:"MR_host"`
}
type JBMailList struct {
	MailElement []MRElement `xml:"element"`
}
type JBHard struct {
	HardResourceRequest []HardElement `xml:"element"`
}
type HardElement struct {
	Name string `xml:"CE_name"`
	ValType int `xml:"CE_valtype"`
	StringVal string `xml:"CE_stringval"`
	DoubleVal float64 `xml:"CE_doubleval"`
	Consumable int `xml:"CE_consumable"`
}
type RangeElement struct {
	RnMin int64 `xml:"RN_min"`
	RnMax int64 `xml:"RN_max"`
	RnStep int64 `xml:"RN_step"`
}
type JBPERange struct {
	Range RangeElement `xml:"element"`
}
type Element struct {
	JbJobNumber int64 `xml:"JB_job_number"`
	JbAr int `xml:"JB_ar"`
	JbExecFile string `xml:"JB_exec_file"`
	JbSubmissionTime string `xml:"JB_submission_time"`
	JbOwner string `xml:"JB_owner"`
	JbUID int `xml:"JB_uid"`
	JbGroup string `xml:"JB_group"`
	JbGid int `xml:"JB_gid"`
	JbAccount string `xml:"JB_account"`
	JbMergeStderr bool `xml:"JB_merge_stderr"`
	JbNotify string `xml:"JB_notify"`
	JbJobName string `xml:"JB_job_name"`
	JbJobShare int `xml:"JB_job_share"`
	JbEnvList JBEnvList `xml:"JB_env_list"`
	JbJobArgs JBJobArgs `xml:"JB_job_args"`
	JbScriptFile string `xml:"JB_script_file"`
	JbJaTasks JBJaTasks `xml:"JB_ja_tasks"`
	JbDeadline int64 `xml:"JB_deadline"`
	JbExecutionTime int64 `xml:"JB_execution_time"`
	JbCheckpointAttr string `xml:"JB_checkpoint_attr"`
	JbCheckpointIntvl int `xml:"JB_checkpoint_interval"`
	JbReserve bool `xml:"JB_reserve"`
	JbMailOptions string `xml:"JB_mail_options"`
	JbPriority int `xml:"JB_priority"`
	JbRestart int `xml:"JB_restart"`
	JbVerify int `xml:"JB_verify"`
	JbScriptSize int `xml:"JB_script_size"`
	JbVerifySuitableQueues int `xml:"JB_verify_suitable_queues"`
	JbSoftWallclockGmt int `xml:"JB_soft_wallclock_gmt"`
	JbHardWallclockGmt int `xml:"JB_hard_wallclock_gmt"`
	JbHardResourceRequest JBHard `xml:"JB_hard_resource_list"`
	JbOverrideTickets int `xml:"JB_override_tickets"`
	JbVersion int `xml:"JB_version"`
	JbJaStructure JBJAStructure `xml:"JB_ja_structure"`
	JbType int `xml:"JB_type"`
	JbBinding JBBinding `xml:"JB_binding"`
	JbMbind int `xml:"JB_mbind"`
	JbIsBinary bool `xml:"JB_is_binary"`
	JbNoShell bool `xml:"JB_no_shell"`
	JbIsArray bool `xml:"JB_is_array"`
	JbIsImmediate bool `xml:"JB_is_immediate"`
	JbJcName string `xml:"JB_jc_name"`
	JbMailList JBMailList `xml:"JB_mail_list"`
	JbPe string `xml:"JB_pe"`
	JbPeRange JBPERange `xml:"JB_pe_range"`
}
type DjobInfo struct {
	Element Element `xml:"element"`
}
type InternalJobStatus struct {
	XMLName xml.Name `xml:"detailed_job_info"`
	Jobinf DjobInfo `xml:"djob_info"`
}
type GDIL struct {
	QueueName string
	Slots int
	Ticket float64
	OverrideTicket float64
	FunctionalTicket float64
	SharetreeTicket float64
}
type QstatJob struct {
	JBjobnumber int64 `xml:"JB_job_number"`
	JATprio float64 `xml:"JAT_prio"`
	JBname string `xml:"JB_name"`
	JBowner string `xml:"JB_owner"`
	State string `xml:"state"`
	JATstarttime string `xml:"JAT_start_time"`
	QueueName string `xml:"queue_name"`
	JClassName string `xml:"jclass_name"`
	Slots int64 `xml:"slots"`
}
type QstatJobInfoList struct {
	XMLName xml.Name `xml:"job_info"`
	JobList []QstatJob `xml:"queue_info>job_list"`
}
func GetJobName(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbJobName
}
func GetJobNumber(v *InternalJobStatus) int64 {
	return v.Jobinf.Element.JbJobNumber
}
func GetExecFileName(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbExecFile
}
func GetJobArgs(v *InternalJobStatus) []string {
	args := make([]string, len(v.Jobinf.Element.JbJobArgs.JBjobArgs))
	for _, i := range v.Jobinf.Element.JbJobArgs.JBjobArgs {
		args = append(args, i.STname)
	}
	return args
}
func GetScriptFile(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbScriptFile
}
func GetAdvanceReservationID(v *InternalJobStatus) int {
	return v.Jobinf.Element.JbAr
}
func GetOwner(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbOwner
}
func GetGroup(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbGroup
}
func GetUID(v *InternalJobStatus) int {
	return v.Jobinf.Element.JbUID
}
func GetGID(v *InternalJobStatus) int {
	return v.Jobinf.Element.JbGid
}
func GetAR(v *InternalJobStatus) int {
	return v.Jobinf.Element.JbAr
}
func GetMailOptions(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbMailOptions
}
func GetPosixPriority(v *InternalJobStatus) int {
	return v.Jobinf.Element.JbPriority
}
func GetAccount(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbAccount
}
func IsImmediate(v *InternalJobStatus) bool {
	return v.Jobinf.Element.JbIsImmediate
}
func IsReservation(v *InternalJobStatus) bool {
	return v.Jobinf.Element.JbReserve
}
func IsBinary(v *InternalJobStatus) bool {
	return v.Jobinf.Element.JbIsBinary
}
func IsNoShell(v *InternalJobStatus) bool {
	return v.Jobinf.Element.JbNoShell
}
func IsArray(v *InternalJobStatus) bool {
	return v.Jobinf.Element.JbIsArray
}
func IsMergeStderr(v *InternalJobStatus) bool {
	return v.Jobinf.Element.JbMergeStderr
}
func GetMbind(v *InternalJobStatus) string {
	switch v.Jobinf.Element.JbMbind {
	case '1':
		return "no_bind"
	case '2':
		return "cores"
	case '3':
		return "cores:strict"
	case '4':
		return "round_robin"
	}
	return "no_bind"
}
func GetSubmissionTime(v *InternalJobStatus) time.Time {
	submissionTime := v.Jobinf.Element.JbSubmissionTime
	st, _ := strconv.Atoi(submissionTime)
	return Unix((int64)(st), 0)
}
func GetExecutionTime(v *InternalJobStatus) time.Time {
	return Unix(v.Jobinf.Element.JbExecutionTime, 0)
}
func GetStartTime(v *InternalJobStatus) time.Time {
	return GetTaskStartTime(v, 0)
}
func GetTaskStartTime(v *InternalJobStatus, taskID int) time.Time {
	size := len(v.Jobinf.Element.JbJaTasks.JaTaskSublist)
	size82 := len(v.Jobinf.Element.JbJaTasks.JaTaskSublist82)
	if taskID >= (size+size82) || taskID < 0 {
		return Unix(0, 0)
	}
	var startTime int64
	if size > size82 {
		startTime = v.Jobinf.Element.JbJaTasks.JaTaskSublist[taskID].JATstarttime
	} else {
		startTime = v.Jobinf.Element.JbJaTasks.JaTaskSublist82[taskID].JATstarttime
	}
	return Unix(startTime, 0)
}
func GetDeadline(v *InternalJobStatus) time.Time {
	return Unix(v.Jobinf.Element.JbDeadline, 0)
}
func GetJobClassName(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbJcName
}
func GetMailingAdresses(v *InternalJobStatus) []string {
	list := make([]string, 1)
	for _, el := range v.Jobinf.Element.JbMailList.MailElement {
		address := fmt.Sprintf("%s@%s", el.User, el.Host)
		list = append(list, address)
	}
	return list
}
func GetParallelEnvironmentRequest(v *InternalJobStatus) string {
	return v.Jobinf.Element.JbPe
}
func GetParallelEnvironmentMin(v *InternalJobStatus) int64 {
	return v.Jobinf.Element.JbPeRange.Range.RnMin
}
func GetParallelEnvironmentMax(v *InternalJobStatus) int64 {
	return v.Jobinf.Element.JbPeRange.Range.RnMax
}
func GetParallelEnvironmentStep(v *InternalJobStatus) int64 {
	return v.Jobinf.Element.JbPeRange.Range.RnStep
}
func GetUsageList(v *InternalJobStatus, task int) (resource []string, value []string) {
	if len(v.Jobinf.Element.JbJaTasks.JaTaskSublist) > task {
		var scaled []JATScaledElements
		if len(v.Jobinf.Element.JbJaTasks.JaTaskSublist[task].JATscaledusagelist.JATscaled) <= 0 {
			scaled = v.Jobinf.Element.JbJaTasks.JaTaskSublist[task].JATscaledusagelist.JATscaled82
		} else {
			scaled = v.Jobinf.Element.JbJaTasks.JaTaskSublist[task].JATscaledusagelist.JATscaled
		}
		for _, v := range scaled {
			resource = append(resource, v.UAname)
			value = append(value, v.UAvalue)
		}
	}
	return resource, value
}
func GetHardRequests(v *InternalJobStatus) (nm []string, val []string) {
	for _, name := range v.Jobinf.Element.JbHardResourceRequest.HardResourceRequest {
		nm = append(nm, name.Name)
		val = append(val, name.StringVal)
	}
	return nm, val
}
func GetTaskCount(v *InternalJobStatus) int {
	taskList := v.Jobinf.Element.JbJaTasks.JaTaskSublist
	taskList82 := v.Jobinf.Element.JbJaTasks.JaTaskSublist82
	return len(taskList) + len(taskList82)
}
func GetGDIL(v *InternalJobStatus, task int) *[]GDIL {
	var taskList []JBJaSublist
	taskList = v.Jobinf.Element.JbJaTasks.JaTaskSublist
	taskListSize := len(taskList)
	if taskListSize <= 0 {
		taskList = v.Jobinf.Element.JbJaTasks.JaTaskSublist82
		taskListSize = len(taskList)
	}
	if task < 0 || task >= taskListSize {
		return nil
	}
	gdil := make([]GDIL, len(taskList[task].JATgranteddestinidentifierlist.GdilElement))
	for i, gdi := range taskList[task].JATgranteddestinidentifierlist.GdilElement {
		gdil[i].FunctionalTicket = gdi.JGfticket
		gdil[i].OverrideTicket = gdi.JGoticket
		gdil[i].QueueName = gdi.JGqname
		gdil[i].SharetreeTicket = gdi.JGsticket
		gdil[i].Slots = gdi.JGslots
		gdil[i].Ticket = gdi.JGticket
	}
	return &gdil
}