diff --git a/Makefile b/Makefile index 72be367..1013648 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ run-privileged: build run: build @echo "Running the container..." mkdir -p ./installation - docker run -p 7070:7070 --rm -it -h master --name $(CONTAINER_NAME) -v ./installation:/opt/cs-install -v ./:/root/go/src/github.com/hpc-gridware/go-clusterscheduler $(IMAGE_NAME):$(IMAGE_TAG) /bin/bash + docker run -p 7070:7070 -p 9464:9464 --rm -it -h master --name $(CONTAINER_NAME) -v ./installation:/opt/cs-install -v ./:/root/go/src/github.com/hpc-gridware/go-clusterscheduler $(IMAGE_NAME):$(IMAGE_TAG) /bin/bash # Running apptainers in containers requires more permissions. You can drop # the --privileged flag and the --cap-add SYS_ADMIN flag if you don't need diff --git a/pkg/qstat/v9.0/parser.go b/pkg/qstat/v9.0/parser.go index 690db20..6e40623 100644 --- a/pkg/qstat/v9.0/parser.go +++ b/pkg/qstat/v9.0/parser.go @@ -766,3 +766,67 @@ func ParseJobInfo(out string) ([]JobInfo, error) { } return jobInfos, nil } + +// ParseClusterQueueSummary parses the output of qstat -g c +func ParseClusterQueueSummary(out string) ([]ClusterQueueSummary, error) { + var summaries []ClusterQueueSummary + lines := strings.Split(out, "\n") + + // Skip the header lines + for _, line := range lines[2:] { + fields := strings.Fields(line) + if len(fields) != 8 { + continue // Skip lines that don't have the expected number of fields + } + + cqLoad, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, fmt.Errorf("failed to parse CQLoad: %v", err) + } + + used, err := strconv.Atoi(fields[2]) + if err != nil { + return nil, fmt.Errorf("failed to parse Used: %v", err) + } + + reserved, err := strconv.Atoi(fields[3]) + if err != nil { + return nil, fmt.Errorf("failed to parse Reserved: %v", err) + } + + available, err := strconv.Atoi(fields[4]) + if err != nil { + return nil, fmt.Errorf("failed to parse Available: %v", err) + } + + total, err := strconv.Atoi(fields[5]) + if err != nil { + return nil, fmt.Errorf("failed to parse Total: %v", err) + } + + aoACDS, err := strconv.Atoi(fields[6]) + if err != nil { + return nil, fmt.Errorf("failed to parse AoACDS: %v", err) + } + + cdsuE, err := strconv.Atoi(fields[7]) + if err != nil { + return nil, fmt.Errorf("failed to parse CdsuE: %v", err) + } + + summary := ClusterQueueSummary{ + ClusterQueue: fields[0], + CQLoad: cqLoad, + Used: used, + Reserved: reserved, + Available: available, + Total: total, + AoACDS: aoACDS, + CdsuE: cdsuE, + } + + summaries = append(summaries, summary) + } + + return summaries, nil +} diff --git a/pkg/qstat/v9.0/parser_test.go b/pkg/qstat/v9.0/parser_test.go index 7ffb408..b45f211 100644 --- a/pkg/qstat/v9.0/parser_test.go +++ b/pkg/qstat/v9.0/parser_test.go @@ -420,4 +420,36 @@ var _ = Describe("Parser", func() { }) + Describe("ClusterQueueSummary", func() { + + It("should parse the output of qstat -g c", func() { + input := `CLUSTER QUEUE CQLOAD USED RES AVAIL TOTAL aoACDS cdsuE +-------------------------------------------------------------------------------- +all.q 0.08 0 0 4 4 0 0 +test.q 0.08 0 0 2 2 0 0` + summary, err := qstat.ParseClusterQueueSummary(input) + Expect(err).NotTo(HaveOccurred()) + Expect(len(summary)).To(Equal(2)) + + Expect(summary[0].ClusterQueue).To(Equal("all.q")) + Expect(summary[0].CQLoad).To(Equal(0.08)) + Expect(summary[0].Used).To(Equal(0)) + Expect(summary[0].Reserved).To(Equal(0)) + Expect(summary[0].Available).To(Equal(4)) + Expect(summary[0].Total).To(Equal(4)) + Expect(summary[0].AoACDS).To(Equal(0)) + Expect(summary[0].CdsuE).To(Equal(0)) + + Expect(summary[1].ClusterQueue).To(Equal("test.q")) + Expect(summary[1].CQLoad).To(Equal(0.08)) + Expect(summary[1].Used).To(Equal(0)) + Expect(summary[1].Reserved).To(Equal(0)) + Expect(summary[1].Available).To(Equal(2)) + Expect(summary[1].Total).To(Equal(2)) + Expect(summary[1].AoACDS).To(Equal(0)) + Expect(summary[1].CdsuE).To(Equal(0)) + }) + + }) + }) diff --git a/pkg/qstat/v9.0/qstat.go b/pkg/qstat/v9.0/qstat.go index 9fd2ff0..09c569f 100644 --- a/pkg/qstat/v9.0/qstat.go +++ b/pkg/qstat/v9.0/qstat.go @@ -43,6 +43,7 @@ type QStat interface { ShowFullOutput() ([]JobInfo, error) // qstat -F ShowFullOutputWithResources(resourceAttributes string) ([]JobInfo, error) + // qstat -g c DisplayClusterQueueSummary() ([]ClusterQueueSummary, error) DisplayAllJobArrayTasks() ([]JobArrayTask, error) DisplayAllParallelJobTasks() ([]ParallelJobTask, error) diff --git a/pkg/qstat/v9.0/qstat_impl.go b/pkg/qstat/v9.0/qstat_impl.go index 9e7ee5f..983b713 100644 --- a/pkg/qstat/v9.0/qstat_impl.go +++ b/pkg/qstat/v9.0/qstat_impl.go @@ -169,8 +169,13 @@ func (q *QStatImpl) ShowFullOutputWithResources(resourceAttributes string) ([]Jo return nil, fmt.Errorf("not implemented") } +// DisplayClusterQueueSummary is equivalent to "qstat -g c" func (q *QStatImpl) DisplayClusterQueueSummary() ([]ClusterQueueSummary, error) { - return nil, fmt.Errorf("not implemented") + out, err := q.NativeSpecification([]string{"-g", "c"}) + if err != nil { + return nil, fmt.Errorf("failed to get output of qstat: %w", err) + } + return ParseClusterQueueSummary(out) } func (q *QStatImpl) DisplayAllJobArrayTasks() ([]JobArrayTask, error) {