From 85d11f6b280bcefde900ec205e5dead519c19e7a Mon Sep 17 00:00:00 2001 From: Eric Garcia Date: Wed, 1 Apr 2026 11:29:26 -0500 Subject: [PATCH] use table format --- cmd/slurm-tracker/main.go | 7 + internal/config/config.go | 1 + internal/slurm/detect.go | 61 -------- internal/slurm/slurm.go | 286 ++++++++++++++++++++++++------------ internal/slurm/v23_02.go | 154 ------------------- internal/slurm/v23_11.go | 190 ------------------------ internal/tracker/tracker.go | 3 - 7 files changed, 200 insertions(+), 502 deletions(-) delete mode 100644 internal/slurm/detect.go delete mode 100644 internal/slurm/v23_02.go delete mode 100644 internal/slurm/v23_11.go diff --git a/cmd/slurm-tracker/main.go b/cmd/slurm-tracker/main.go index 91d9f03..5fa6b7b 100644 --- a/cmd/slurm-tracker/main.go +++ b/cmd/slurm-tracker/main.go @@ -25,6 +25,7 @@ var ( func main() { // Configure zerolog zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + zerolog.SetGlobalLevel(zerolog.InfoLevel) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) if err := rootCmd.Execute(); err != nil { @@ -41,6 +42,11 @@ var rootCmd = &cobra.Command{ } func preRun(cmd *cobra.Command, args []string) error { + if cfg.Debug { + zerolog.SetGlobalLevel(zerolog.DebugLevel) + log.Debug().Msg("Debug logging enabled") + } + // Validate required flags apiKey, _ := cmd.Flags().GetString("api-key") if apiKey == "" { @@ -153,6 +159,7 @@ func init() { rootCmd.Flags().StringVar(&cfg.PlatformHost, "api-server", os.Getenv("PW_PLATFORM_HOST"), "Platform host to send api requests to (defaults to PW_PLATFORM_HOST env var)") rootCmd.Flags().StringVar(&cfg.StateFile, "state-file", "", "File to store running job states (defaults to ./slurm_job_states.db)") rootCmd.Flags().StringVar(&cfg.ConfigFilePath, "config", "config.json", "Path to config file with account/allocation mappings") + rootCmd.Flags().BoolVarP(&cfg.Debug, "debug", "d", false, "Enable debug logging") // not checking error since its not possible here // required flags are checked in preRun diff --git a/internal/config/config.go b/internal/config/config.go index d7043f1..e606ec8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -33,6 +33,7 @@ type Config struct { OrganizationName string LookbackMinutes int DryRun bool + Debug bool PlatformHost string StateFile string ConfigFilePath string diff --git a/internal/slurm/detect.go b/internal/slurm/detect.go deleted file mode 100644 index 1cf4e5d..0000000 --- a/internal/slurm/detect.go +++ /dev/null @@ -1,61 +0,0 @@ -package slurm - -import ( - "fmt" - "os/exec" - "strconv" - "strings" -) - -type slurmVersion struct { - Major int - Minor int - Micro int -} - -// detectVersion runs `sinfo --version` and parses the Slurm version string. -// The output format is: "slurm 23.11.9" -func detectVersion() (slurmVersion, error) { - cmd := exec.Command("sinfo", "--version") - out, err := cmd.Output() - if err != nil { - return slurmVersion{}, fmt.Errorf("sinfo --version failed: %w", err) - } - - line := strings.TrimSpace(string(out)) - // Expected format: "slurm 23.11.9" - parts := strings.Fields(line) - if len(parts) < 2 { - return slurmVersion{}, fmt.Errorf("unexpected sinfo --version output: %q", line) - } - - return parseVersionString(parts[1]) -} - -// parseVersionString parses a version string like "23.11.9" into a slurmVersion. -func parseVersionString(s string) (slurmVersion, error) { - parts := strings.SplitN(s, ".", 3) - if len(parts) < 2 { - return slurmVersion{}, fmt.Errorf("cannot parse version %q", s) - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return slurmVersion{}, fmt.Errorf("invalid major version in %q: %w", s, err) - } - - minor, err := strconv.Atoi(parts[1]) - if err != nil { - return slurmVersion{}, fmt.Errorf("invalid minor version in %q: %w", s, err) - } - - micro := 0 - if len(parts) == 3 { - micro, err = strconv.Atoi(parts[2]) - if err != nil { - return slurmVersion{}, fmt.Errorf("invalid micro version in %q: %w", s, err) - } - } - - return slurmVersion{Major: major, Minor: minor, Micro: micro}, nil -} diff --git a/internal/slurm/slurm.go b/internal/slurm/slurm.go index 06c0cff..c3ebf40 100644 --- a/internal/slurm/slurm.go +++ b/internal/slurm/slurm.go @@ -4,115 +4,79 @@ import ( "bytes" "fmt" "os/exec" + "strconv" + "strings" "time" "github.com/rs/zerolog/log" ) -// NumberValue represents a value that can be set/infinite/number -type NumberValue struct { - Set bool `json:"set"` - Infinite bool `json:"infinite"` - Number int `json:"number"` +// sacctFields defines the fields requested from sacct in the order they appear. +var sacctFields = []string{ + "JobIDRaw", + "User", + "Account", + "Partition", + + "JobName", + "State", + "ExitCode", + "Start", + "End", + "ElapsedRaw", + "AllocCPUS", + "AllocNodes", + "NodeList", + "AllocTRES", + "Cluster", } // TresAlloc represents a TRES allocation entry type TresAlloc struct { - Type string `json:"type"` - Name string `json:"name"` - ID int `json:"id"` - Count int `json:"count"` + Type string + Name string + ID int + Count int } // Job is the canonical normalized job type used throughout the application. -// Version-specific parsers normalize their output into this type. type Job struct { - JobID int `json:"job_id"` - Name string `json:"name"` - User string `json:"user"` - Account string `json:"account"` - AllocationNodes int `json:"allocation_nodes"` - State struct { - Current string `json:"current"` // always a plain string (normalized from []string in 23.11) - Reason string `json:"reason"` - } `json:"state"` - Partition string `json:"partition"` - QOS string `json:"qos"` - ExitCode struct { - Status string `json:"status"` // normalized from []string in 23.11 - ReturnCode int `json:"return_code"` // normalized from NumberValue in 23.11 - } `json:"exit_code"` + JobID int + Name string + User string + Account string + AllocationNodes int + Partition string + + Cluster string + Nodes string + State struct { + Current string + } + ExitCode struct { + ReturnCode int + Signal int + } Time struct { - Submission int64 `json:"submission"` - Start int64 `json:"start"` - End int64 `json:"end"` - Elapsed int `json:"elapsed"` - Eligible int64 `json:"eligible"` - Suspended int `json:"suspended"` - Limit NumberValue `json:"limit"` - Planned NumberValue `json:"planned"` - System struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"system"` - Total struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"total"` - User struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"user"` - } `json:"time"` - Association struct { - Account string `json:"account"` - Cluster string `json:"cluster"` - Partition string `json:"partition"` - User string `json:"user"` - ID int `json:"id"` - } `json:"association"` - Cluster string `json:"cluster"` - Group string `json:"group"` - Nodes string `json:"nodes"` + Start int64 + End int64 + Elapsed int + } Required struct { - CPUs int `json:"CPUs"` - MemoryPerCPU NumberValue `json:"memory_per_cpu"` - MemoryPerNode NumberValue `json:"memory_per_node"` - } `json:"required"` + CPUs int + } Tres struct { - Allocated []TresAlloc `json:"allocated"` - Requested []TresAlloc `json:"requested"` - } `json:"tres"` - Flags []string `json:"flags"` - WorkingDirectory string `json:"working_directory"` - SubmitLine string `json:"submit_line"` + Allocated []TresAlloc + } } -// GetJobs detects the Slurm version, then queries sacct and returns normalized jobs. +// GetJobs queries sacct and returns parsed jobs. func GetJobs(lookbackMinutes int) ([]Job, error) { - v, err := detectVersion() + data, err := runSacct(lookbackMinutes) if err != nil { - return nil, fmt.Errorf("failed to detect Slurm version: %w", err) - } - - log.Info(). - Int("major", v.Major). - Int("minor", v.Minor). - Int("micro", v.Micro). - Msg("Detected Slurm version") - - switch { - case v.Major == 23 && v.Minor <= 2: - return getJobsV2302(lookbackMinutes) - default: - if v.Major != 23 || v.Minor != 11 { - log.Warn(). - Int("major", v.Major). - Int("minor", v.Minor). - Msg("Untested Slurm version, falling back to 23.11 parser") - } - return getJobsV2311(lookbackMinutes) + return nil, err } + return parseSacctOutput(data) } // GetCurrentState returns the normalized state string for a job. @@ -156,8 +120,7 @@ func CalculateCoreHoursForElapsed(job *Job, elapsedSeconds int) float64 { return float64(allocatedCPUs) * elapsedHours } -// runSacct executes the sacct command and returns its raw JSON output. -// The sacct flags are identical across all supported Slurm versions. +// runSacct executes the sacct command and returns its raw parsable2 output. func runSacct(lookbackMinutes int) ([]byte, error) { startTime := time.Now().Add(-time.Duration(lookbackMinutes) * time.Minute) startTimeStr := startTime.Format("2006-01-02T15:04:05") @@ -166,16 +129,21 @@ func runSacct(lookbackMinutes int) ([]byte, error) { Str("start_time", startTimeStr). Msg("Querying sacct") - cmd := exec.Command("sacct", + args := []string{ "--parsable2", "--allocations", "--units=M", "--allusers", "-S", startTimeStr, "-E", "now", - "-o", "JobIDRaw,JobID,User,Account,Partition,QOS,JobName,State,ExitCode,Submit,Start,End,ElapsedRaw,AllocCPUS,AllocNodes,CPUTimeRAW,MaxRSS,AveRSS,NodeList,AllocTRES", - "--json", - ) + "-o", strings.Join(sacctFields, ","), + } + + log.Debug(). + Str("command", "sacct "+strings.Join(args, " ")). + Msg("Running sacct command") + + cmd := exec.Command("sacct", args...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout @@ -191,3 +159,133 @@ func runSacct(lookbackMinutes int) ([]byte, error) { return stdout.Bytes(), nil } + +// parseSacctOutput parses the pipe-delimited parsable2 output from sacct. +func parseSacctOutput(data []byte) ([]Job, error) { + text := strings.TrimSpace(string(data)) + if text == "" { + return nil, nil + } + + lines := strings.Split(text, "\n") + if len(lines) < 2 { + return nil, nil + } + + // First line is the header + header := strings.Split(lines[0], "|") + fieldIndex := make(map[string]int, len(header)) + for i, h := range header { + fieldIndex[h] = i + } + var jobs []Job + for _, line := range lines[1:] { + if line == "" { + continue + } + fields := strings.Split(line, "|") + if len(fields) != len(header) { + log.Warn(). + Str("line", line). + Int("expected_fields", len(header)). + Int("actual_fields", len(fields)). + Msg("Skipping malformed sacct line") + continue + } + + get := func(name string) string { + if idx, ok := fieldIndex[name]; ok { + return fields[idx] + } + return "" + } + + var job Job + job.JobID, _ = strconv.Atoi(get("JobIDRaw")) + job.User = get("User") + job.Account = get("Account") + job.Partition = get("Partition") + + job.Name = get("JobName") + job.State.Current = parseState(get("State")) + job.ExitCode.ReturnCode, job.ExitCode.Signal = parseExitCode(get("ExitCode")) + job.Time.Start = parseTimestamp(get("Start")) + job.Time.End = parseTimestamp(get("End")) + job.Time.Elapsed, _ = strconv.Atoi(get("ElapsedRaw")) + job.Required.CPUs, _ = strconv.Atoi(get("AllocCPUS")) + job.AllocationNodes, _ = strconv.Atoi(get("AllocNodes")) + job.Nodes = get("NodeList") + job.Tres.Allocated = parseTRES(get("AllocTRES")) + job.Cluster = get("Cluster") + + log.Debug(). + Int("job_id", job.JobID). + Str("user", job.User). + Str("account", job.Account). + Str("partition", job.Partition). + Str("state", job.State.Current). + Int("elapsed", job.Time.Elapsed). + Int("cpus", job.Required.CPUs). + Str("nodes", job.Nodes). + Msg("Parsed job") + + jobs = append(jobs, job) + } + + return jobs, nil +} + +// parseState extracts the state keyword from sacct state strings. +// sacct can return values like "CANCELLED by 1000" — only the first word is the state. +func parseState(s string) string { + if i := strings.IndexByte(s, ' '); i >= 0 { + return s[:i] + } + return s +} + +// parseExitCode parses sacct's "returncode:signal" format (e.g. "0:0"). +func parseExitCode(s string) (returnCode, signal int) { + parts := strings.SplitN(s, ":", 2) + returnCode, _ = strconv.Atoi(parts[0]) + if len(parts) == 2 { + signal, _ = strconv.Atoi(parts[1]) + } + return +} + +// parseTimestamp parses sacct timestamp strings into Unix timestamps. +// Returns 0 for empty, "Unknown", or "None" values. +func parseTimestamp(s string) int64 { + if s == "" || s == "Unknown" || s == "None" { + return 0 + } + t, err := time.ParseInLocation("2006-01-02T15:04:05", s, time.Local) + if err != nil { + log.Warn().Str("timestamp", s).Err(err).Msg("Failed to parse sacct timestamp") + return 0 + } + return t.Unix() +} + +// parseTRES parses sacct's AllocTRES format (e.g. "billing=4,cpu=4,mem=16000M,node=1"). +func parseTRES(s string) []TresAlloc { + if s == "" { + return nil + } + var allocs []TresAlloc + for _, part := range strings.Split(s, ",") { + kv := strings.SplitN(part, "=", 2) + if len(kv) != 2 { + continue + } + // Strip unit suffixes (e.g., "16000M" -> 16000) + numStr := strings.TrimRight(kv[1], "GMKTBPgmktbp") + count, _ := strconv.Atoi(numStr) + allocs = append(allocs, TresAlloc{ + Type: kv[0], + Count: count, + }) + } + return allocs +} diff --git a/internal/slurm/v23_02.go b/internal/slurm/v23_02.go deleted file mode 100644 index 00071be..0000000 --- a/internal/slurm/v23_02.go +++ /dev/null @@ -1,154 +0,0 @@ -package slurm - -import ( - "encoding/json" - "fmt" -) - -// sacctOutput2302 matches the sacct --json output format for Slurm 23.02.x. -// Version fields are integers, and State.Current is a plain string. -type sacctOutput2302 struct { - Meta struct { - Slurm struct { - Version struct { - Major int `json:"major"` - Minor int `json:"minor"` - Micro int `json:"micro"` - Release string `json:"release"` - Cluster string `json:"cluster"` - } `json:"version"` - } `json:"Slurm"` - } `json:"meta"` - Jobs []job2302 `json:"jobs"` - Errors []any `json:"errors"` - Warnings []any `json:"warnings"` -} - -type job2302 struct { - JobID int `json:"job_id"` - Name string `json:"name"` - User string `json:"user"` - Account string `json:"account"` - AllocationNodes int `json:"allocation_nodes"` - State struct { - Current string `json:"current"` - Reason string `json:"reason"` - } `json:"state"` - Partition string `json:"partition"` - QOS string `json:"qos"` - ExitCode struct { - Status string `json:"status"` - ReturnCode int `json:"return_code"` - } `json:"exit_code"` - Time struct { - Submission int64 `json:"submission"` - Start int64 `json:"start"` - End int64 `json:"end"` - Elapsed int `json:"elapsed"` - Eligible int64 `json:"eligible"` - Suspended int `json:"suspended"` - Limit NumberValue `json:"limit"` - Planned NumberValue `json:"planned"` - System struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"system"` - Total struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"total"` - User struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"user"` - } `json:"time"` - Association struct { - Account string `json:"account"` - Cluster string `json:"cluster"` - Partition string `json:"partition"` - User string `json:"user"` - ID int `json:"id"` - } `json:"association"` - Cluster string `json:"cluster"` - Group string `json:"group"` - Nodes string `json:"nodes"` - Required struct { - CPUs int `json:"CPUs"` - MemoryPerCPU NumberValue `json:"memory_per_cpu"` - MemoryPerNode NumberValue `json:"memory_per_node"` - } `json:"required"` - Tres struct { - Allocated []TresAlloc `json:"allocated"` - Requested []TresAlloc `json:"requested"` - } `json:"tres"` - Flags []string `json:"flags"` - WorkingDirectory string `json:"working_directory"` - SubmitLine string `json:"submit_line"` -} - -// getJobsV2302 runs sacct and returns normalized jobs for Slurm 23.02.x. -func getJobsV2302(lookbackMinutes int) ([]Job, error) { - data, err := runSacct(lookbackMinutes) - if err != nil { - return nil, err - } - - var output sacctOutput2302 - if err := json.Unmarshal(data, &output); err != nil { - return nil, fmt.Errorf("failed to parse sacct output (23.02 format): %w", err) - } - - jobs := make([]Job, len(output.Jobs)) - for i, j := range output.Jobs { - jobs[i] = normalize2302(j) - } - return jobs, nil -} - -// normalize2302 converts a 23.02.x job to the canonical Job type. -// This version's format is closest to canonical, so the mapping is direct. -func normalize2302(j job2302) Job { - var out Job - out.JobID = j.JobID - out.Name = j.Name - out.User = j.User - out.Account = j.Account - out.AllocationNodes = j.AllocationNodes - out.State.Current = j.State.Current - out.State.Reason = j.State.Reason - out.Partition = j.Partition - out.QOS = j.QOS - out.ExitCode.Status = j.ExitCode.Status - out.ExitCode.ReturnCode = j.ExitCode.ReturnCode - out.Time.Submission = j.Time.Submission - out.Time.Start = j.Time.Start - out.Time.End = j.Time.End - out.Time.Elapsed = j.Time.Elapsed - out.Time.Eligible = j.Time.Eligible - out.Time.Suspended = j.Time.Suspended - out.Time.Limit = j.Time.Limit - out.Time.Planned = j.Time.Planned - out.Time.System.Seconds = j.Time.System.Seconds - out.Time.System.Microseconds = j.Time.System.Microseconds - out.Time.Total.Seconds = j.Time.Total.Seconds - out.Time.Total.Microseconds = j.Time.Total.Microseconds - out.Time.User.Seconds = j.Time.User.Seconds - out.Time.User.Microseconds = j.Time.User.Microseconds - out.Association.Account = j.Association.Account - out.Association.Cluster = j.Association.Cluster - out.Association.Partition = j.Association.Partition - out.Association.User = j.Association.User - out.Association.ID = j.Association.ID - out.Cluster = j.Cluster - out.Group = j.Group - out.Nodes = j.Nodes - out.Required.CPUs = j.Required.CPUs - out.Required.MemoryPerCPU = j.Required.MemoryPerCPU - out.Required.MemoryPerNode = j.Required.MemoryPerNode - out.Tres.Allocated = j.Tres.Allocated - out.Tres.Requested = j.Tres.Requested - out.Flags = j.Flags - out.WorkingDirectory = j.WorkingDirectory - out.SubmitLine = j.SubmitLine - return out -} diff --git a/internal/slurm/v23_11.go b/internal/slurm/v23_11.go deleted file mode 100644 index e15f463..0000000 --- a/internal/slurm/v23_11.go +++ /dev/null @@ -1,190 +0,0 @@ -package slurm - -import ( - "encoding/json" - "fmt" -) - -// sacctOutput2311 matches the sacct --json output format for Slurm 23.11.x. -// Version fields are strings, State.Current is []string, and ExitCode uses NumberValue. -type sacctOutput2311 struct { - Meta struct { - Plugin struct { - Type string `json:"type"` - Name string `json:"name"` - DataParser string `json:"data_parser"` - AccountingStorage string `json:"accounting_storage"` - } `json:"plugin"` - Client struct { - Source string `json:"source"` - User string `json:"user"` - Group string `json:"group"` - } `json:"client"` - Command []string `json:"command"` - Slurm struct { - Version struct { - Major string `json:"major"` - Minor string `json:"minor"` - Micro string `json:"micro"` - } `json:"version"` - Release string `json:"release"` - Cluster string `json:"cluster"` - } `json:"slurm"` - } `json:"meta"` - Jobs []job2311 `json:"jobs"` - Errors []any `json:"errors"` - Warnings []any `json:"warnings"` -} - -type exitCode2311 struct { - Status []string `json:"status"` - ReturnCode NumberValue `json:"return_code"` - Signal struct { - ID NumberValue `json:"id"` - Name string `json:"name"` - } `json:"signal"` -} - -type job2311 struct { - JobID int `json:"job_id"` - Name string `json:"name"` - User string `json:"user"` - Account string `json:"account"` - AllocationNodes int `json:"allocation_nodes"` - State struct { - Current []string `json:"current"` - Reason string `json:"reason"` - } `json:"state"` - Partition string `json:"partition"` - QOS string `json:"qos"` - ExitCode exitCode2311 `json:"exit_code"` - DerivedExitCode exitCode2311 `json:"derived_exit_code"` - Time struct { - Submission int64 `json:"submission"` - Start int64 `json:"start"` - End int64 `json:"end"` - Elapsed int `json:"elapsed"` - Eligible int64 `json:"eligible"` - Suspended int `json:"suspended"` - Limit NumberValue `json:"limit"` - Planned NumberValue `json:"planned"` - System struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"system"` - Total struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"total"` - User struct { - Seconds int `json:"seconds"` - Microseconds int `json:"microseconds"` - } `json:"user"` - } `json:"time"` - Association struct { - Account string `json:"account"` - Cluster string `json:"cluster"` - Partition string `json:"partition"` - User string `json:"user"` - ID int `json:"id"` - } `json:"association"` - Cluster string `json:"cluster"` - Group string `json:"group"` - Nodes string `json:"nodes"` - Required struct { - CPUs int `json:"CPUs"` - MemoryPerCPU NumberValue `json:"memory_per_cpu"` - MemoryPerNode NumberValue `json:"memory_per_node"` - } `json:"required"` - Tres struct { - Allocated []TresAlloc `json:"allocated"` - Requested []TresAlloc `json:"requested"` - } `json:"tres"` - Flags []string `json:"flags"` - WorkingDirectory string `json:"working_directory"` - SubmitLine string `json:"submit_line"` -} - -// getJobsV2311 runs sacct and returns normalized jobs for Slurm 23.11.x. -func getJobsV2311(lookbackMinutes int) ([]Job, error) { - data, err := runSacct(lookbackMinutes) - if err != nil { - return nil, err - } - - var output sacctOutput2311 - if err := json.Unmarshal(data, &output); err != nil { - return nil, fmt.Errorf("failed to parse sacct output (23.11 format): %w", err) - } - - jobs := make([]Job, len(output.Jobs)) - for i, j := range output.Jobs { - jobs[i] = normalize2311(j) - } - return jobs, nil -} - -// normalize2311 converts a 23.11.x job to the canonical Job type. -// Key normalizations: -// - State.Current: first element of []string, or "" if empty -// - ExitCode.Status: first element of []string, or "" if empty -// - ExitCode.ReturnCode: NumberValue.Number -func normalize2311(j job2311) Job { - var out Job - out.JobID = j.JobID - out.Name = j.Name - out.User = j.User - out.Account = j.Account - out.AllocationNodes = j.AllocationNodes - - if len(j.State.Current) > 0 { - out.State.Current = j.State.Current[0] - } - out.State.Reason = j.State.Reason - - out.Partition = j.Partition - out.QOS = j.QOS - - if len(j.ExitCode.Status) > 0 { - out.ExitCode.Status = j.ExitCode.Status[0] - } - out.ExitCode.ReturnCode = j.ExitCode.ReturnCode.Number - - out.Time.Submission = j.Time.Submission - out.Time.Start = j.Time.Start - out.Time.End = j.Time.End - out.Time.Elapsed = j.Time.Elapsed - out.Time.Eligible = j.Time.Eligible - out.Time.Suspended = j.Time.Suspended - out.Time.Limit = j.Time.Limit - out.Time.Planned = j.Time.Planned - out.Time.System.Seconds = j.Time.System.Seconds - out.Time.System.Microseconds = j.Time.System.Microseconds - out.Time.Total.Seconds = j.Time.Total.Seconds - out.Time.Total.Microseconds = j.Time.Total.Microseconds - out.Time.User.Seconds = j.Time.User.Seconds - out.Time.User.Microseconds = j.Time.User.Microseconds - - out.Association.Account = j.Association.Account - out.Association.Cluster = j.Association.Cluster - out.Association.Partition = j.Association.Partition - out.Association.User = j.Association.User - out.Association.ID = j.Association.ID - - out.Cluster = j.Cluster - out.Group = j.Group - out.Nodes = j.Nodes - - out.Required.CPUs = j.Required.CPUs - out.Required.MemoryPerCPU = j.Required.MemoryPerCPU - out.Required.MemoryPerNode = j.Required.MemoryPerNode - - out.Tres.Allocated = j.Tres.Allocated - out.Tres.Requested = j.Tres.Requested - - out.Flags = j.Flags - out.WorkingDirectory = j.WorkingDirectory - out.SubmitLine = j.SubmitLine - - return out -} diff --git a/internal/tracker/tracker.go b/internal/tracker/tracker.go index 3955f50..b9e703f 100644 --- a/internal/tracker/tracker.go +++ b/internal/tracker/tracker.go @@ -128,9 +128,6 @@ func ProcessJob(cfg *config.Config, job *slurm.Job, stateDriver *state.Driver, p if job.Account != "" { metadata["account"] = job.Account } - if job.QOS != "" { - metadata["qos"] = job.QOS - } usageEvent := parallelworks.PostUsageEventInput{ Quantity: coreHours,