fix: xcecli jobs list -l — hide spurious start line and show pending reason

Three fixes:

1. ExecHosts.UnmarshalJSON: JSON null (sent when no allocation exists)
   was treated as a non-"-" string because json.Unmarshal(null, &str)
   returns nil with str="". This created a phantom HostSlot{NumSlots:1,
   HostName:""} producing "<1*>" for every PEND job. Now null and ""
   are both treated as empty hosts.

2. page_print_jobs.go: added !startTime.IsZero() guard so a zero
   timestamp ("0001-01-01T00:00:00Z") never produces a start line
   even if the status check is bypassed.

3. Added PENDING_REASON field to JobFieldsMap so it is requested from
   the API when -l is passed, and rendered in printJobsVerboseOutput
   for PEND/PSUSP jobs before the start-time section.
This commit is contained in:
Rongsong Shen 2026-05-09 05:15:31 +00:00
parent af578c29ca
commit a2476d0176
2 changed files with 26 additions and 5 deletions

View file

@ -322,9 +322,20 @@ func printJobsVerboseOutput(jobs *models.JobsResponse, filter []string) {
}
PrintWrappedLine(w, submitLine, 80, 18)
// (3) print the start time info
// (3) print the pending reason for jobs waiting to be dispatched
if job.JobStatus == "PEND" || job.JobStatus == "PSUSP" {
if job.PendingReason != "" {
fmt.Fprintf(w, " PENDING REASONS:\n")
fmt.Fprintf(w, " %s\n", job.PendingReason)
}
}
// (4) print the start time info — skip for jobs that haven't started yet
startLine := ""
if startTime, err := parseJobTime(job.StartTime); err == nil {
if job.JobStatus == "PEND" || job.JobStatus == "PSUSP" {
goto printEndLine
}
if startTime, err := parseJobTime(job.StartTime); err == nil && !startTime.IsZero() {
month := startTime.Format("Jan")
day := startTime.Format("02")
exec_hostname := ""
@ -380,6 +391,7 @@ func printJobsVerboseOutput(jobs *models.JobsResponse, filter []string) {
}
PrintWrappedLine(w, startLine, 80, 18)
printEndLine:
// (4) print end line
endLine := ""
if job.JobStatus == "DONE" || job.JobStatus == "EXIT" {

View file

@ -32,6 +32,7 @@ const (
HEADER_JOB_LAUNCH_TYPE = "LAUNCH_TYPE"
HEADER_JOB_NUM_PROCESSORS = "NUM_PROCESSORS"
HEADER_JOB_INTERACTIVE_URL = "INTERACTIVE_URL"
HEADER_JOB_PENDING_REASON = "PENDING_REASON"
// second line
HEADER_JOB_R15S = "r15s"
@ -210,6 +211,7 @@ var JobFieldsMap = map[string]string{
HEADER_JOB_LAUNCH_TYPE: "launch_type",
HEADER_JOB_NUM_PROCESSORS: "num_processors",
HEADER_JOB_INTERACTIVE_URL: "interactive_job_url",
HEADER_JOB_PENDING_REASON: "pending_reason",
//job_filter.HEADER_JOB_PRIORITY: "job_priority",
}
@ -258,16 +260,23 @@ func (e ExecHosts) String() string {
// UnmarshalJSON customizes the JSON parsing method, compatible with both string "-" and structure formats
func (e *ExecHosts) UnmarshalJSON(data []byte) error {
// JSON null means no allocation — treat as empty
if string(data) == "null" {
e.HostSlots = []HostSlot{}
e.NumHosts = 0
return nil
}
// First try to parse as a string
var str string
if err := json.Unmarshal(data, &str); err == nil {
// If the string is "-", return an empty ExecHosts
if str == "-" {
// Empty string or "-" both mean no hosts
if str == "" || str == "-" {
e.HostSlots = []HostSlot{}
e.NumHosts = 0
return nil
}
// If it's any other string, treat it as a single hostname
// Any other non-empty string is a single hostname
e.HostSlots = []HostSlot{{HostName: str, NumSlots: 1}}
e.NumHosts = 1
return nil