Skip to content

Add helper function and example for extensible accounting #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,20 @@
#___INFO__MARK_END_NEW__

#FROM hpcgridware/clusterscheduler-latest-ubuntu2204:latest
FROM hpcgridware/clusterscheduler-latest-ubuntu2204:V901_TAG
#FROM hpcgridware/clusterscheduler-latest-ubuntu2204:V901_TAG
#FROM hpcgridware/ocs-ubuntu2204:9.0.2
FROM hpcgridware/ocs-ubuntu2204-nightly:20250203

RUN mkdir -p /opt/helpers

COPY autoinstall.template /opt/helpers/
COPY installer.sh /opt/helpers/
COPY entrypoint.sh /entrypoint.sh

ARG GOLANG_VERSION=1.23.1
ARG GOLANG_VERSION=1.23.5

RUN apt-get update && \
apt-get install -y curl wget git gcc make vim libhwloc-dev hwloc software-properties-common && \
apt-get install -y curl wget git gcc make vim libhwloc-dev hwloc software-properties-common man-db && \
add-apt-repository -y ppa:apptainer/ppa && \
apt-get update && \
apt-get install -y apptainer
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CONTAINER_NAME = $(IMAGE_NAME)
.PHONY: build
build:
@echo "Building the Open Cluster Scheduler image..."
docker build -t $(IMAGE_NAME):$(IMAGE_TAG) .
docker build --platform=linux/amd64 -t $(IMAGE_NAME):$(IMAGE_TAG) .

# 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
Expand All @@ -43,7 +43,7 @@ run: build
@echo "Running the Open Cluster Scheduler container..."
@echo "For a new installation, you need to remove the ./installation subdirectory first."
mkdir -p ./installation
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
docker run --platform=linux/amd64 -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
Expand Down
56 changes: 56 additions & 0 deletions examples/flexableaccounting/flexibleaccounting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"fmt"

"github.com/hpc-gridware/go-clusterscheduler/pkg/accounting"
"golang.org/x/exp/rand"
)

// This is an example epilog which adds arbitrary accounting records for
// jobs to the system. In order to use the example additional accounting records
// for the "test" namespace - a namespace is just a subsection in the JSON
// accounting file - (with usage values with the "tst prefix) needs to
// be enabled:
//
// qconf -mconf
// ..
// reporting_params ... usage_patterns=test:tst*
//
// Compile this example (go build) and copy the binary to your cluster
// scheduler installation directory.
//
// Then configure your queue epilog script with sgeadmin@/path/to/flexibleaccounting
// Ensure that the binary is executable. The sgeadmin is the correct user
// to use for this (check the owner of $SGE_ROOT).
//
// When configured correctly the following command should output the
// accounting records:
//
// qacct -j <job_id>
//
// You should see the two tst_random* records in the qacct after your job
// has finished.
//
// tst_random1 351.000
// tst_random2 121.000
func main() {
usageFilePath, err := accounting.GetUsageFilePath()
if err != nil {
fmt.Printf("Failed to get usage file path: %v\n", err)
return
}
err = accounting.AppendToAccounting(usageFilePath, []accounting.Record{
{
AccountingKey: "tst_random1",
AccountingValue: rand.Intn(1000),
},
{
AccountingKey: "tst_random2",
AccountingValue: rand.Intn(1000),
},
})
if err != nil {
fmt.Printf("Failed to append to accounting: %v\n", err)
}
}
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
go.opentelemetry.io/otel/sdk v1.33.0
go.opentelemetry.io/otel/sdk/log v0.9.0
go.opentelemetry.io/otel/sdk/metric v1.33.0
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c
)

require (
Expand All @@ -27,9 +28,9 @@ require (
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.26.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCt
go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
6 changes: 5 additions & 1 deletion installer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ fi
echo "Open Cluster Scheduler is not yet installed in ${MOUNT_DIR}. Starting installation."

# Copy unpacked Open Cluster Scheduler package to ${MOUNT_DIR}
cp -r /opt/cs/* ${MOUNT_DIR}
if [ -d /opt/ocs ]; then
cp -r /opt/ocs/* "${MOUNT_DIR}"
else
cp -r /opt/cs/* "${MOUNT_DIR}"
fi

cd ${MOUNT_DIR}

Expand Down
65 changes: 65 additions & 0 deletions pkg/accounting/accounting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*___INFO__MARK_BEGIN__*/
/*************************************************************************
* Copyright 2025 HPC-Gridware GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************/
/*___INFO__MARK_END__*/

package accounting

import (
"fmt"
"os"
"path/filepath"
)

// Record is a key-value pair representing an accounting record.
type Record struct {
AccountingKey string
AccountingValue int
}

// AppendToAccounting appends accounting records to the usage file so
// that it gets send to the execution daemon. Typically sgeadmin user
// (Cluster Scheduler install user).
// Hence you need to prefix your epilog script with sgeadmin@/path/to/epilog.
func AppendToAccounting(usageFilePath string, records []Record) error {
f, err := os.OpenFile(usageFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()

var nl string

for _, record := range records {
nl += fmt.Sprintf("%s=%d\n",
record.AccountingKey,
record.AccountingValue)
}

_, err = f.WriteString(nl)
return err
}

// GetUsageFilePath returns the path to the job usage file in the
// job spool directory.
func GetUsageFilePath() (string, error) {
jobSpoolDir := os.Getenv("SGE_JOB_SPOOL_DIR")
if jobSpoolDir == "" {
return "", fmt.Errorf("SGE_JOB_SPOOL_DIR is not set")
}
return filepath.Join(jobSpoolDir, "usage"), nil
}
32 changes: 32 additions & 0 deletions pkg/accounting/accounting_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*___INFO__MARK_BEGIN__*/
/*************************************************************************
* Copyright 2025 HPC-Gridware GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************/
/*___INFO__MARK_END__*/

package accounting_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestAccounting(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Accounting Suite")
}
50 changes: 50 additions & 0 deletions pkg/accounting/accounting_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*___INFO__MARK_BEGIN__*/
/*************************************************************************
* Copyright 2025 HPC-Gridware GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************/
/*___INFO__MARK_END__*/

package accounting_test

import (
"os"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/hpc-gridware/go-clusterscheduler/pkg/accounting"
)

var _ = Describe("Accounting", func() {

Context("GetUsageFilePath", func() {

It("should return the usage file path", func() {
os.Unsetenv("SGE_JOB_SPOOL_DIR")
usageFilePath, err := accounting.GetUsageFilePath()
Expect(err).To(HaveOccurred())
Expect(usageFilePath).To(Equal(""))
})

It("should return the usage file path", func() {
os.Setenv("SGE_JOB_SPOOL_DIR", "/var/spool/gridengine/job_spool")
usageFilePath, err := accounting.GetUsageFilePath()
Expect(err).NotTo(HaveOccurred())
Expect(usageFilePath).To(Equal("/var/spool/gridengine/job_spool/usage"))
})

})
})
Loading