mirror of
https://github.com/ClusterCockpit/cc-metric-collector.git
synced 2025-10-20 21:05:06 +02:00
Compare commits
44 Commits
v0.7.2
...
move_examp
Author | SHA1 | Date | |
---|---|---|---|
|
ce9e21c48e | ||
|
6243203880 | ||
|
c7c9f8c273 | ||
|
6a4ad067ac | ||
|
ed2378f794 | ||
|
99e066ff5f | ||
|
67cdbefb02 | ||
|
b522aca693 | ||
|
ea7c4f4ec7 | ||
|
09cf89a951 | ||
|
d6499935a4 | ||
|
3e19c47ae4 | ||
|
97e09f13f4 | ||
|
e08bd3d926 | ||
|
fc525b7430 | ||
|
69d4567ecf | ||
|
c5183feafc | ||
|
a45366646e | ||
|
a551616566 | ||
|
a9fa168117 | ||
|
39d37597ab | ||
|
aeaba0021b | ||
|
5ceffb44b4 | ||
|
e29942a4be | ||
|
0b9b9a6e68 | ||
|
b47cb3a0c4 | ||
|
b49ae7b612 | ||
|
1fc5cc8483 | ||
|
e81099af8d | ||
|
eaca327d73 | ||
|
2e48996d87 | ||
|
7cdbada522 | ||
|
babe1e020d | ||
|
776af72231 | ||
|
2d4894b8f7 | ||
|
35295b0b3a | ||
|
1e734baa35 | ||
|
aa6181a018 | ||
|
0a2a85f2ce | ||
|
ec34b40295 | ||
|
8ccbb4f69c | ||
|
f90c2698e3 | ||
|
ee4e1baf5b | ||
|
94c80307e8 |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
14
README.md
14
README.md
@@ -32,12 +32,14 @@ There is a main configuration file with basic settings that point to the other c
|
||||
|
||||
``` json
|
||||
{
|
||||
"sinks": "sinks.json",
|
||||
"collectors" : "collectors.json",
|
||||
"receivers" : "receivers.json",
|
||||
"router" : "router.json",
|
||||
"interval": "10s",
|
||||
"duration": "1s"
|
||||
"sinks-file": "sinks.json",
|
||||
"collectors-file" : "collectors.json",
|
||||
"receivers-file" : "receivers.json",
|
||||
"router-file" : "router.json",
|
||||
"main": {
|
||||
"interval": "10s",
|
||||
"duration": "1s"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -52,6 +52,7 @@ In contrast to the configuration files for sinks and receivers, the collectors c
|
||||
* [`beegfs_meta`](./beegfsmetaMetric.md)
|
||||
* [`beegfs_storage`](./beegfsstorageMetric.md)
|
||||
* [`rocm_smi`](./rocmsmiMetric.md)
|
||||
* [`slurm_cgroup`](./slurmCgroupMetric.md)
|
||||
|
||||
## Todos
|
||||
|
||||
|
@@ -47,6 +47,7 @@ var AvailableCollectors = map[string]MetricCollector{
|
||||
"self": new(SelfCollector),
|
||||
"schedstat": new(SchedstatCollector),
|
||||
"nfsiostat": new(NfsIOStatCollector),
|
||||
"slurm_cgroup": new(SlurmCgroupCollector),
|
||||
}
|
||||
|
||||
// Metric collector manager data structure
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
cclog "github.com/ClusterCockpit/cc-lib/ccLogger"
|
||||
@@ -27,8 +29,17 @@ import (
|
||||
const DEFAULT_GPFS_CMD = "mmpmon"
|
||||
|
||||
type GpfsCollectorLastState struct {
|
||||
bytesRead int64
|
||||
bytesWritten int64
|
||||
numOpens int64
|
||||
numCloses int64
|
||||
numReads int64
|
||||
numWrites int64
|
||||
numReaddirs int64
|
||||
numInodeUpdates int64
|
||||
bytesRead int64
|
||||
bytesWritten int64
|
||||
bytesTotal int64
|
||||
iops int64
|
||||
metaops int64
|
||||
}
|
||||
|
||||
type GpfsCollector struct {
|
||||
@@ -37,9 +48,12 @@ type GpfsCollector struct {
|
||||
config struct {
|
||||
Mmpmon string `json:"mmpmon_path,omitempty"`
|
||||
ExcludeFilesystem []string `json:"exclude_filesystem,omitempty"`
|
||||
Sudo bool `json:"use_sudo,omitempty"`
|
||||
SendBandwidths bool `json:"send_bandwidths"`
|
||||
SendTotalValues bool `json:"send_total_values"`
|
||||
SendDerivedValues bool `json:"send_derived_values"`
|
||||
}
|
||||
sudoCmd string
|
||||
skipFS map[string]struct{}
|
||||
lastTimestamp time.Time // Store time stamp of last tick to derive bandwidths
|
||||
lastState map[string]GpfsCollectorLastState
|
||||
@@ -82,18 +96,43 @@ func (m *GpfsCollector) Init(config json.RawMessage) error {
|
||||
m.lastState = make(map[string]GpfsCollectorLastState)
|
||||
|
||||
// GPFS / IBM Spectrum Scale file system statistics can only be queried by user root
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get current user: %v", err)
|
||||
}
|
||||
if user.Uid != "0" {
|
||||
return fmt.Errorf("GPFS file system statistics can only be queried by user root")
|
||||
if !m.config.Sudo {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Failed to get current user:", err.Error())
|
||||
return err
|
||||
}
|
||||
if user.Uid != "0" {
|
||||
cclog.ComponentError(m.name, "GPFS file system statistics can only be queried by user root")
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
p, err := exec.LookPath("sudo")
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Cannot find 'sudo'")
|
||||
return err
|
||||
}
|
||||
m.sudoCmd = p
|
||||
}
|
||||
|
||||
// when using sudo, the full path of mmpmon must be specified because
|
||||
// exec.LookPath will not work as mmpmon is not executable as user
|
||||
if m.config.Sudo && !strings.HasPrefix(m.config.Mmpmon, "/") {
|
||||
return fmt.Errorf("when using sudo, mmpmon_path must be provided and an absolute path: %s", m.config.Mmpmon)
|
||||
}
|
||||
|
||||
// Check if mmpmon is in executable search path
|
||||
p, err := exec.LookPath(m.config.Mmpmon)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find mmpmon binary '%s': %v", m.config.Mmpmon, err)
|
||||
// if using sudo, exec.lookPath will return EACCES (file mode r-x------), this can be ignored
|
||||
if m.config.Sudo && errors.Is(err, syscall.EACCES) {
|
||||
cclog.ComponentWarn(m.name, fmt.Sprintf("got error looking for mmpmon binary '%s': %v . This is expected when using sudo, continuing.", m.config.Mmpmon, err))
|
||||
// the file was given in the config, use it
|
||||
p = m.config.Mmpmon
|
||||
} else {
|
||||
cclog.ComponentError(m.name, fmt.Sprintf("failed to find mmpmon binary '%s': %v", m.config.Mmpmon, err))
|
||||
return fmt.Errorf("failed to find mmpmon binary '%s': %v", m.config.Mmpmon, err)
|
||||
}
|
||||
}
|
||||
m.config.Mmpmon = p
|
||||
|
||||
@@ -118,7 +157,13 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
// -p: generate output that can be parsed
|
||||
// -s: suppress the prompt on input
|
||||
// fs_io_s: Displays I/O statistics per mounted file system
|
||||
cmd := exec.Command(m.config.Mmpmon, "-p", "-s")
|
||||
var cmd *exec.Cmd
|
||||
if m.config.Sudo {
|
||||
cmd = exec.Command(m.sudoCmd, m.config.Mmpmon, "-p", "-s")
|
||||
} else {
|
||||
cmd = exec.Command(m.config.Mmpmon, "-p", "-s")
|
||||
}
|
||||
|
||||
cmd.Stdin = strings.NewReader("once fs_io_s\n")
|
||||
cmdStdout := new(bytes.Buffer)
|
||||
cmdStderr := new(bytes.Buffer)
|
||||
@@ -185,6 +230,22 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
if m.config.SendDerivedValues {
|
||||
if _, ok := m.lastState[filesystem]; !ok {
|
||||
m.lastState[filesystem] = GpfsCollectorLastState{
|
||||
numReads: -1,
|
||||
numWrites: -1,
|
||||
numOpens: -1,
|
||||
numCloses: -1,
|
||||
numReaddirs: -1,
|
||||
numInodeUpdates: -1,
|
||||
bytesTotal: -1,
|
||||
iops: -1,
|
||||
metaops: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return code
|
||||
rc, err := strconv.Atoi(key_value["_rc_"])
|
||||
if err != nil {
|
||||
@@ -278,7 +339,7 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendBandwidths {
|
||||
if lastBytesWritten := m.lastState[filesystem].bytesRead; lastBytesWritten >= 0 {
|
||||
if lastBytesWritten := m.lastState[filesystem].bytesWritten; lastBytesWritten >= 0 {
|
||||
bwWrite := float64(bytesWritten-lastBytesWritten) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
@@ -296,13 +357,6 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
if m.config.SendBandwidths {
|
||||
m.lastState[filesystem] = GpfsCollectorLastState{
|
||||
bytesRead: bytesRead,
|
||||
bytesWritten: bytesWritten,
|
||||
}
|
||||
}
|
||||
|
||||
// number of opens
|
||||
numOpens, err := strconv.ParseInt(key_value["_oc_"], 10, 64)
|
||||
if err != nil {
|
||||
@@ -314,6 +368,24 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
if y, err := lp.NewMessage("gpfs_num_opens", m.tags, m.meta, map[string]interface{}{"value": numOpens}, timestamp); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastNumOpens := m.lastState[filesystem].numOpens; lastNumOpens >= 0 {
|
||||
opensRate := float64(numOpens-lastNumOpens) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_opens_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": opensRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// number of closes
|
||||
numCloses, err := strconv.ParseInt(key_value["_cc_"], 10, 64)
|
||||
@@ -326,6 +398,24 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
if y, err := lp.NewMessage("gpfs_num_closes", m.tags, m.meta, map[string]interface{}{"value": numCloses}, timestamp); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastNumCloses := m.lastState[filesystem].numCloses; lastNumCloses >= 0 {
|
||||
closesRate := float64(numCloses-lastNumCloses) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_closes_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": closesRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// number of reads
|
||||
numReads, err := strconv.ParseInt(key_value["_rdc_"], 10, 64)
|
||||
@@ -338,6 +428,24 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
if y, err := lp.NewMessage("gpfs_num_reads", m.tags, m.meta, map[string]interface{}{"value": numReads}, timestamp); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastNumReads := m.lastState[filesystem].numReads; lastNumReads >= 0 {
|
||||
readsRate := float64(numReads-lastNumReads) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_reads_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": readsRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// number of writes
|
||||
numWrites, err := strconv.ParseInt(key_value["_wc_"], 10, 64)
|
||||
@@ -350,6 +458,24 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
if y, err := lp.NewMessage("gpfs_num_writes", m.tags, m.meta, map[string]interface{}{"value": numWrites}, timestamp); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastNumWrites := m.lastState[filesystem].numWrites; lastNumWrites >= 0 {
|
||||
writesRate := float64(numWrites-lastNumWrites) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_writes_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": writesRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// number of read directories
|
||||
numReaddirs, err := strconv.ParseInt(key_value["_dir_"], 10, 64)
|
||||
@@ -362,6 +488,24 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
if y, err := lp.NewMessage("gpfs_num_readdirs", m.tags, m.meta, map[string]interface{}{"value": numReaddirs}, timestamp); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastNumReaddirs := m.lastState[filesystem].numReaddirs; lastNumReaddirs >= 0 {
|
||||
readdirsRate := float64(numReaddirs-lastNumReaddirs) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_readdirs_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": readdirsRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Number of inode updates
|
||||
numInodeUpdates, err := strconv.ParseInt(key_value["_iu_"], 10, 64)
|
||||
@@ -374,10 +518,31 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
if y, err := lp.NewMessage("gpfs_num_inode_updates", m.tags, m.meta, map[string]interface{}{"value": numInodeUpdates}, timestamp); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastNumInodeUpdates := m.lastState[filesystem].numInodeUpdates; lastNumInodeUpdates >= 0 {
|
||||
inodeUpdatesRate := float64(numInodeUpdates-lastNumInodeUpdates) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_inode_updates_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": inodeUpdatesRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Total values
|
||||
bytesTotal := int64(-1);
|
||||
iops := int64(-1);
|
||||
metaops := int64(-1);
|
||||
if m.config.SendTotalValues {
|
||||
bytesTotal := bytesRead + bytesWritten
|
||||
bytesTotal = bytesRead + bytesWritten
|
||||
if y, err :=
|
||||
lp.NewMessage("gpfs_bytes_total",
|
||||
m.tags,
|
||||
@@ -390,7 +555,26 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
y.AddMeta("unit", "bytes")
|
||||
output <- y
|
||||
}
|
||||
iops := numReads + numWrites
|
||||
if m.config.SendBandwidths {
|
||||
if lastBytesTotal := m.lastState[filesystem].bytesTotal; lastBytesTotal >= 0 {
|
||||
bwTotal := float64(bytesTotal-lastBytesTotal) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_bw_total",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": bwTotal,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "bytes/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iops = numReads + numWrites
|
||||
if y, err :=
|
||||
lp.NewMessage("gpfs_iops",
|
||||
m.tags,
|
||||
@@ -402,7 +586,26 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
); err == nil {
|
||||
output <- y
|
||||
}
|
||||
metaops := numInodeUpdates + numCloses + numOpens + numReaddirs
|
||||
if m.config.SendDerivedValues {
|
||||
if lastIops := m.lastState[filesystem].iops; lastIops >= 0 {
|
||||
iopsRate := float64(iops-lastIops) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_iops_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": iopsRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaops = numInodeUpdates + numCloses + numOpens + numReaddirs
|
||||
if y, err :=
|
||||
lp.NewMessage("gpfs_metaops",
|
||||
m.tags,
|
||||
@@ -414,7 +617,41 @@ func (m *GpfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
); err == nil {
|
||||
output <- y
|
||||
}
|
||||
if m.config.SendDerivedValues {
|
||||
if lastMetaops := m.lastState[filesystem].metaops; lastMetaops >= 0 {
|
||||
metaopsRate := float64(metaops-lastMetaops) / timeDiff
|
||||
if y, err :=
|
||||
lp.NewMessage(
|
||||
"gpfs_metaops_rate",
|
||||
m.tags,
|
||||
m.meta,
|
||||
map[string]interface{}{
|
||||
"value": metaopsRate,
|
||||
},
|
||||
timestamp,
|
||||
); err == nil {
|
||||
y.AddMeta("unit", "requests/sec")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save last state
|
||||
m.lastState[filesystem] = GpfsCollectorLastState{
|
||||
bytesRead: bytesRead,
|
||||
bytesWritten: bytesWritten,
|
||||
numOpens: numOpens,
|
||||
numCloses: numCloses,
|
||||
numReads: numReads,
|
||||
numWrites: numWrites,
|
||||
numReaddirs: numReaddirs,
|
||||
numInodeUpdates: numInodeUpdates,
|
||||
bytesTotal: bytesTotal,
|
||||
iops: iops,
|
||||
metaops: metaops,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,11 +14,13 @@ hugo_path: docs/reference/cc-metric-collector/collectors/gpfs.md
|
||||
```json
|
||||
"gpfs": {
|
||||
"mmpmon_path": "/path/to/mmpmon",
|
||||
"use_sudo": "true",
|
||||
"exclude_filesystem": [
|
||||
"fs1"
|
||||
],
|
||||
"send_bandwidths": true,
|
||||
"send_total_values": true
|
||||
"send_total_values": true,
|
||||
"send_derived_values": true
|
||||
}
|
||||
```
|
||||
|
||||
@@ -31,6 +33,11 @@ in the configuration.
|
||||
The path to the `mmpmon` command can be configured with the `mmpmon_path` option
|
||||
in the configuration. If nothing is set, the collector searches in `$PATH` for `mmpmon`.
|
||||
|
||||
If cc-metric-collector is run as non-root, `sudo` can be enabled with `use_sudo`.
|
||||
Because `mmpmon` is by default only executable as root, the Go procedure to
|
||||
search for it in `$PATH` will fail. If you use `sudo`, you must specify the
|
||||
complete path for `mmpmon` using the parameter `mmpmon_path`.
|
||||
|
||||
|
||||
Metrics:
|
||||
* `gpfs_bytes_read`
|
||||
@@ -41,10 +48,19 @@ Metrics:
|
||||
* `gpfs_num_writes`
|
||||
* `gpfs_num_readdirs`
|
||||
* `gpfs_num_inode_updates`
|
||||
* `gpfs_opens_rate` (if `send_derived_values == true`)
|
||||
* `gpfs_closes_rate` (if `send_derived_values == true`)
|
||||
* `gpfs_reads_rate` (if `send_derived_values == true`)
|
||||
* `gpfs_writes_rate` (if `send_derived_values == true`)
|
||||
* `gpfs_readdirs_rate` (if `send_derived_values == true`)
|
||||
* `gpfs_inode_updates_rate` (if `send_derived_values == true`)
|
||||
* `gpfs_bytes_total = gpfs_bytes_read + gpfs_bytes_written` (if `send_total_values == true`)
|
||||
* `gpfs_iops = gpfs_num_reads + gpfs_num_writes` (if `send_total_values == true`)
|
||||
* `gpfs_iops_rate` (if `send_total_values == true` and `send_derived_values == true`)
|
||||
* `gpfs_metaops = gpfs_num_inode_updates + gpfs_num_closes + gpfs_num_opens + gpfs_num_readdirs` (if `send_total_values == true`)
|
||||
* `gpfs_metaops_rate` (if `send_total_values == true` and `send_derived_values == true`)
|
||||
* `gpfs_bw_read` (if `send_bandwidths == true`)
|
||||
* `gpfs_bw_write` (if `send_bandwidths == true`)
|
||||
* `gpfs_bw_total` (if `send_bandwidths == true` and `send_total_values == true`)
|
||||
|
||||
The collector adds a `filesystem` tag to all metrics
|
||||
|
@@ -20,18 +20,17 @@ import (
|
||||
lp "github.com/ClusterCockpit/cc-lib/ccMessage"
|
||||
)
|
||||
|
||||
// Konstante für den Pfad zu /proc/diskstats
|
||||
const IOSTATFILE = `/proc/diskstats`
|
||||
|
||||
type IOstatCollectorConfig struct {
|
||||
ExcludeMetrics []string `json:"exclude_metrics,omitempty"`
|
||||
// Neues Feld zum Ausschließen von Devices per JSON-Konfiguration
|
||||
ExcludeDevices []string `json:"exclude_devices,omitempty"`
|
||||
}
|
||||
|
||||
type IOstatCollectorEntry struct {
|
||||
lastValues map[string]int64
|
||||
tags map[string]string
|
||||
currentValues map[string]int64
|
||||
lastValues map[string]int64
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
type IOstatCollector struct {
|
||||
@@ -105,16 +104,27 @@ func (m *IOstatCollector) Init(config json.RawMessage) error {
|
||||
if _, skip := stringArrayContains(m.config.ExcludeDevices, device); skip {
|
||||
continue
|
||||
}
|
||||
values := make(map[string]int64)
|
||||
currentValues := make(map[string]int64)
|
||||
lastValues := make(map[string]int64)
|
||||
for m := range m.matches {
|
||||
values[m] = 0
|
||||
currentValues[m] = 0
|
||||
lastValues[m] = 0
|
||||
}
|
||||
for name, idx := range m.matches {
|
||||
if idx < len(linefields) {
|
||||
if value, err := strconv.ParseInt(linefields[idx], 0, 64); err == nil {
|
||||
currentValues[name] = value
|
||||
lastValues[name] = value // Set last to current for first read
|
||||
}
|
||||
}
|
||||
}
|
||||
m.devices[device] = IOstatCollectorEntry{
|
||||
tags: map[string]string{
|
||||
"device": device,
|
||||
"type": "node",
|
||||
},
|
||||
lastValues: values,
|
||||
currentValues: currentValues,
|
||||
lastValues: lastValues,
|
||||
}
|
||||
}
|
||||
m.init = true
|
||||
@@ -153,18 +163,22 @@ func (m *IOstatCollector) Read(interval time.Duration, output chan lp.CCMessage)
|
||||
if _, ok := m.devices[device]; !ok {
|
||||
continue
|
||||
}
|
||||
// Update current and last values
|
||||
entry := m.devices[device]
|
||||
for name, idx := range m.matches {
|
||||
if idx < len(linefields) {
|
||||
x, err := strconv.ParseInt(linefields[idx], 0, 64)
|
||||
if err == nil {
|
||||
diff := x - entry.lastValues[name]
|
||||
y, err := lp.NewMessage(name, entry.tags, m.meta, map[string]interface{}{"value": int(diff)}, time.Now())
|
||||
// Calculate difference using previous current and new value
|
||||
diff := x - entry.currentValues[name]
|
||||
y, err := lp.NewMetric(name, entry.tags, m.meta, int(diff), time.Now())
|
||||
if err == nil {
|
||||
output <- y
|
||||
}
|
||||
// Update last to previous current, and current to new value
|
||||
entry.lastValues[name] = entry.currentValues[name]
|
||||
entry.currentValues[name] = x
|
||||
}
|
||||
entry.lastValues[name] = x
|
||||
}
|
||||
}
|
||||
m.devices[device] = entry
|
||||
|
@@ -102,8 +102,11 @@ func (m *NUMAStatsCollector) Init(config json.RawMessage) error {
|
||||
file := filepath.Join(dir, "numastat")
|
||||
m.topology = append(m.topology,
|
||||
NUMAStatsCollectorTopolgy{
|
||||
file: file,
|
||||
tagSet: map[string]string{"memoryDomain": node},
|
||||
file: file,
|
||||
tagSet: map[string]string{
|
||||
"type": "memoryDomain",
|
||||
"type-id": node,
|
||||
},
|
||||
previousValues: make(map[string]int64),
|
||||
})
|
||||
}
|
||||
|
@@ -589,7 +589,7 @@ func readMaxClocks(device *NvidiaCollectorDevice, output chan lp.CCMessage) erro
|
||||
if !device.excludeMetrics["nv_max_graphics_clock"] {
|
||||
max_gclk, ret := nvml.DeviceGetMaxClockInfo(device.device, nvml.CLOCK_GRAPHICS)
|
||||
if ret == nvml.SUCCESS {
|
||||
y, err := lp.NewMessage("nv_max_graphics_clock", device.tags, device.meta, map[string]interface{}{"value": float64(max_gclk)}, time.Now())
|
||||
y, err := lp.NewMetric("nv_max_graphics_clock", device.tags, device.meta, float64(max_gclk), time.Now())
|
||||
if err == nil {
|
||||
y.AddMeta("unit", "MHz")
|
||||
output <- y
|
||||
@@ -598,9 +598,9 @@ func readMaxClocks(device *NvidiaCollectorDevice, output chan lp.CCMessage) erro
|
||||
}
|
||||
|
||||
if !device.excludeMetrics["nv_max_sm_clock"] {
|
||||
maxSmClock, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_SM)
|
||||
maxSmClock, ret := nvml.DeviceGetMaxClockInfo(device.device, nvml.CLOCK_SM)
|
||||
if ret == nvml.SUCCESS {
|
||||
y, err := lp.NewMessage("nv_max_sm_clock", device.tags, device.meta, map[string]interface{}{"value": float64(maxSmClock)}, time.Now())
|
||||
y, err := lp.NewMetric("nv_max_sm_clock", device.tags, device.meta, float64(maxSmClock), time.Now())
|
||||
if err == nil {
|
||||
y.AddMeta("unit", "MHz")
|
||||
output <- y
|
||||
@@ -609,9 +609,9 @@ func readMaxClocks(device *NvidiaCollectorDevice, output chan lp.CCMessage) erro
|
||||
}
|
||||
|
||||
if !device.excludeMetrics["nv_max_mem_clock"] {
|
||||
maxMemClock, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_MEM)
|
||||
maxMemClock, ret := nvml.DeviceGetMaxClockInfo(device.device, nvml.CLOCK_MEM)
|
||||
if ret == nvml.SUCCESS {
|
||||
y, err := lp.NewMessage("nv_max_mem_clock", device.tags, device.meta, map[string]interface{}{"value": float64(maxMemClock)}, time.Now())
|
||||
y, err := lp.NewMetric("nv_max_mem_clock", device.tags, device.meta, float64(maxMemClock), time.Now())
|
||||
if err == nil {
|
||||
y.AddMeta("unit", "MHz")
|
||||
output <- y
|
||||
@@ -620,9 +620,9 @@ func readMaxClocks(device *NvidiaCollectorDevice, output chan lp.CCMessage) erro
|
||||
}
|
||||
|
||||
if !device.excludeMetrics["nv_max_video_clock"] {
|
||||
maxMemClock, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_VIDEO)
|
||||
maxVideoClock, ret := nvml.DeviceGetMaxClockInfo(device.device, nvml.CLOCK_VIDEO)
|
||||
if ret == nvml.SUCCESS {
|
||||
y, err := lp.NewMessage("nv_max_video_clock", device.tags, device.meta, map[string]interface{}{"value": float64(maxMemClock)}, time.Now())
|
||||
y, err := lp.NewMetric("nv_max_video_clock", device.tags, device.meta, float64(maxVideoClock), time.Now())
|
||||
if err == nil {
|
||||
y.AddMeta("unit", "MHz")
|
||||
output <- y
|
||||
|
349
collectors/slurmCgroupMetric.go
Normal file
349
collectors/slurmCgroupMetric.go
Normal file
@@ -0,0 +1,349 @@
|
||||
package collectors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cclog "github.com/ClusterCockpit/cc-lib/ccLogger"
|
||||
lp "github.com/ClusterCockpit/cc-lib/ccMessage"
|
||||
)
|
||||
|
||||
type SlurmJobData struct {
|
||||
MemoryUsage float64
|
||||
MaxMemoryUsage float64
|
||||
LimitMemoryUsage float64
|
||||
CpuUsageUser float64
|
||||
CpuUsageSys float64
|
||||
CpuSet []int
|
||||
}
|
||||
|
||||
type SlurmCgroupsConfig struct {
|
||||
CgroupBase string `json:"cgroup_base"`
|
||||
ExcludeMetrics []string `json:"exclude_metrics,omitempty"`
|
||||
UseSudo bool `json:"use_sudo,omitempty"`
|
||||
}
|
||||
|
||||
type SlurmCgroupCollector struct {
|
||||
metricCollector
|
||||
config SlurmCgroupsConfig
|
||||
meta map[string]string
|
||||
tags map[string]string
|
||||
allCPUs []int
|
||||
cpuUsed map[int]bool
|
||||
cgroupBase string
|
||||
excludeMetrics map[string]struct{}
|
||||
useSudo bool
|
||||
}
|
||||
|
||||
const defaultCgroupBase = "/sys/fs/cgroup/system.slice/slurmstepd.scope"
|
||||
|
||||
func ParseCPUs(cpuset string) ([]int, error) {
|
||||
var result []int
|
||||
if cpuset == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
ranges := strings.Split(cpuset, ",")
|
||||
for _, r := range ranges {
|
||||
if strings.Contains(r, "-") {
|
||||
parts := strings.Split(r, "-")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid CPU range: %s", r)
|
||||
}
|
||||
start, err := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid CPU range start: %s", parts[0])
|
||||
}
|
||||
end, err := strconv.Atoi(strings.TrimSpace(parts[1]))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid CPU range end: %s", parts[1])
|
||||
}
|
||||
for i := start; i <= end; i++ {
|
||||
result = append(result, i)
|
||||
}
|
||||
} else {
|
||||
cpu, err := strconv.Atoi(strings.TrimSpace(r))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid CPU ID: %s", r)
|
||||
}
|
||||
result = append(result, cpu)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetAllCPUs() ([]int, error) {
|
||||
data, err := os.ReadFile("/sys/devices/system/cpu/online")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read /sys/devices/system/cpu/online: %v", err)
|
||||
}
|
||||
return ParseCPUs(strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
func (m *SlurmCgroupCollector) isExcluded(metric string) bool {
|
||||
_, found := m.excludeMetrics[metric]
|
||||
return found
|
||||
}
|
||||
|
||||
func (m *SlurmCgroupCollector) readFile(path string) ([]byte, error) {
|
||||
if m.useSudo {
|
||||
cmd := exec.Command("sudo", "cat", path)
|
||||
return cmd.Output()
|
||||
}
|
||||
return os.ReadFile(path)
|
||||
}
|
||||
|
||||
func (m *SlurmCgroupCollector) Init(config json.RawMessage) error {
|
||||
var err error
|
||||
m.name = "SlurmCgroupCollector"
|
||||
m.setup()
|
||||
m.parallel = true
|
||||
m.meta = map[string]string{"source": m.name, "group": "SLURM"}
|
||||
m.tags = map[string]string{"type": "hwthread"}
|
||||
m.cpuUsed = make(map[int]bool)
|
||||
m.cgroupBase = defaultCgroupBase
|
||||
|
||||
if len(config) > 0 {
|
||||
err = json.Unmarshal(config, &m.config)
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Error reading config:", err.Error())
|
||||
return err
|
||||
}
|
||||
m.excludeMetrics = make(map[string]struct{})
|
||||
for _, metric := range m.config.ExcludeMetrics {
|
||||
m.excludeMetrics[metric] = struct{}{}
|
||||
}
|
||||
if m.config.CgroupBase != "" {
|
||||
m.cgroupBase = m.config.CgroupBase
|
||||
}
|
||||
}
|
||||
|
||||
m.useSudo = m.config.UseSudo
|
||||
if !m.useSudo {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Failed to get current user:", err.Error())
|
||||
return err
|
||||
}
|
||||
if user.Uid != "0" {
|
||||
cclog.ComponentError(m.name, "Reading cgroup files requires root privileges (or enable use_sudo in config)")
|
||||
return fmt.Errorf("not root")
|
||||
}
|
||||
}
|
||||
|
||||
m.allCPUs, err = GetAllCPUs()
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Error reading online CPUs:", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
m.init = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SlurmCgroupCollector) ReadJobData(jobdir string) (SlurmJobData, error) {
|
||||
jobdata := SlurmJobData{
|
||||
MemoryUsage: 0,
|
||||
MaxMemoryUsage: 0,
|
||||
LimitMemoryUsage: 0,
|
||||
CpuUsageUser: 0,
|
||||
CpuUsageSys: 0,
|
||||
CpuSet: []int{},
|
||||
}
|
||||
|
||||
cg := func(f string) string { return filepath.Join(m.cgroupBase, jobdir, f) }
|
||||
|
||||
memUsage, err := m.readFile(cg("memory.current"))
|
||||
if err == nil {
|
||||
x, err := strconv.ParseFloat(strings.TrimSpace(string(memUsage)), 64)
|
||||
if err == nil {
|
||||
jobdata.MemoryUsage = x
|
||||
}
|
||||
}
|
||||
|
||||
maxMem, err := m.readFile(cg("memory.peak"))
|
||||
if err == nil {
|
||||
x, err := strconv.ParseFloat(strings.TrimSpace(string(maxMem)), 64)
|
||||
if err == nil {
|
||||
jobdata.MaxMemoryUsage = x
|
||||
}
|
||||
}
|
||||
|
||||
limitMem, err := m.readFile(cg("memory.max"))
|
||||
if err == nil {
|
||||
x, err := strconv.ParseFloat(strings.TrimSpace(string(limitMem)), 64)
|
||||
if err == nil {
|
||||
jobdata.LimitMemoryUsage = x
|
||||
}
|
||||
}
|
||||
|
||||
cpuStat, err := m.readFile(cg("cpu.stat"))
|
||||
if err == nil {
|
||||
lines := strings.Split(strings.TrimSpace(string(cpuStat)), "\n")
|
||||
var usageUsec, userUsec, systemUsec float64
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
value, err := strconv.ParseFloat(fields[1], 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
switch fields[0] {
|
||||
case "usage_usec":
|
||||
usageUsec = value
|
||||
case "user_usec":
|
||||
userUsec = value
|
||||
case "system_usec":
|
||||
systemUsec = value
|
||||
}
|
||||
}
|
||||
if usageUsec > 0 {
|
||||
jobdata.CpuUsageUser = (userUsec * 100 / usageUsec)
|
||||
jobdata.CpuUsageSys = (systemUsec * 100 / usageUsec)
|
||||
}
|
||||
}
|
||||
|
||||
cpuSet, err := m.readFile(cg("cpuset.cpus"))
|
||||
if err == nil {
|
||||
cpus, err := ParseCPUs(strings.TrimSpace(string(cpuSet)))
|
||||
if err == nil {
|
||||
jobdata.CpuSet = cpus
|
||||
}
|
||||
}
|
||||
|
||||
return jobdata, nil
|
||||
}
|
||||
|
||||
func (m *SlurmCgroupCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
||||
timestamp := time.Now()
|
||||
|
||||
for k := range m.cpuUsed {
|
||||
delete(m.cpuUsed, k)
|
||||
}
|
||||
|
||||
globPattern := filepath.Join(m.cgroupBase, "job_*")
|
||||
jobDirs, err := filepath.Glob(globPattern)
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Error globbing job directories:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, jdir := range jobDirs {
|
||||
jKey := filepath.Base(jdir)
|
||||
|
||||
jobdata, err := m.ReadJobData(jKey)
|
||||
if err != nil {
|
||||
cclog.ComponentError(m.name, "Error reading job data for", jKey, ":", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if len(jobdata.CpuSet) > 0 {
|
||||
coreCount := float64(len(jobdata.CpuSet))
|
||||
for _, cpu := range jobdata.CpuSet {
|
||||
coreTags := map[string]string{
|
||||
"type": "hwthread",
|
||||
"type-id": fmt.Sprintf("%d", cpu),
|
||||
}
|
||||
|
||||
if coreCount > 0 && !m.isExcluded("job_mem_used") {
|
||||
memPerCore := jobdata.MemoryUsage / coreCount
|
||||
if y, err := lp.NewMessage("job_mem_used", coreTags, m.meta, map[string]interface{}{"value": memPerCore}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "Bytes")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if coreCount > 0 && !m.isExcluded("job_max_mem_used") {
|
||||
maxMemPerCore := jobdata.MaxMemoryUsage / coreCount
|
||||
if y, err := lp.NewMessage("job_max_mem_used", coreTags, m.meta, map[string]interface{}{"value": maxMemPerCore}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "Bytes")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if coreCount > 0 && !m.isExcluded("job_mem_limit") {
|
||||
limitPerCore := jobdata.LimitMemoryUsage / coreCount
|
||||
if y, err := lp.NewMessage("job_mem_limit", coreTags, m.meta, map[string]interface{}{"value": limitPerCore}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "Bytes")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if coreCount > 0 && !m.isExcluded("job_user_cpu") {
|
||||
cpuUserPerCore := jobdata.CpuUsageUser / coreCount
|
||||
if y, err := lp.NewMessage("job_user_cpu", coreTags, m.meta, map[string]interface{}{"value": cpuUserPerCore}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "%")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if coreCount > 0 && !m.isExcluded("job_sys_cpu") {
|
||||
cpuSysPerCore := jobdata.CpuUsageSys / coreCount
|
||||
if y, err := lp.NewMessage("job_sys_cpu", coreTags, m.meta, map[string]interface{}{"value": cpuSysPerCore}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "%")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
m.cpuUsed[cpu] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, cpu := range m.allCPUs {
|
||||
if !m.cpuUsed[cpu] {
|
||||
coreTags := map[string]string{
|
||||
"type": "hwthread",
|
||||
"type-id": fmt.Sprintf("%d", cpu),
|
||||
}
|
||||
|
||||
if !m.isExcluded("job_mem_used") {
|
||||
if y, err := lp.NewMessage("job_mem_used", coreTags, m.meta, map[string]interface{}{"value": 0}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "Bytes")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if !m.isExcluded("job_max_mem_used") {
|
||||
if y, err := lp.NewMessage("job_max_mem_used", coreTags, m.meta, map[string]interface{}{"value": 0}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "Bytes")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if !m.isExcluded("job_mem_limit") {
|
||||
if y, err := lp.NewMessage("job_mem_limit", coreTags, m.meta, map[string]interface{}{"value": 0}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "Bytes")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if !m.isExcluded("job_user_cpu") {
|
||||
if y, err := lp.NewMessage("job_user_cpu", coreTags, m.meta, map[string]interface{}{"value": 0}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "%")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
|
||||
if !m.isExcluded("job_sys_cpu") {
|
||||
if y, err := lp.NewMessage("job_sys_cpu", coreTags, m.meta, map[string]interface{}{"value": 0}, timestamp); err == nil {
|
||||
y.AddMeta("unit", "%")
|
||||
output <- y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SlurmCgroupCollector) Close() {
|
||||
m.init = false
|
||||
}
|
50
collectors/slurmCgroupMetric.md
Normal file
50
collectors/slurmCgroupMetric.md
Normal file
@@ -0,0 +1,50 @@
|
||||
<!--
|
||||
---
|
||||
title: Slurm cgroup metric collector
|
||||
description: Collect per-core memory and CPU usage for SLURM jobs from cgroup v2
|
||||
categories: [cc-metric-collector]
|
||||
tags: ['Admin']
|
||||
weight: 3
|
||||
hugo_path: docs/reference/cc-metric-collector/collectors/slurm_cgroup.md
|
||||
---
|
||||
-->
|
||||
|
||||
## `slurm_cgroup` collector
|
||||
|
||||
The `slurm_cgroup` collector reads job-specific resource metrics from the cgroup v2 filesystem and provides **hwthread** metrics for memory and CPU usage of running SLURM jobs.
|
||||
|
||||
### Example configuration
|
||||
|
||||
```json
|
||||
"slurm_cgroup": {
|
||||
"cgroup_base": "/sys/fs/cgroup/system.slice/slurmstepd.scope",
|
||||
"exclude_metrics": [
|
||||
"job_sys_cpu",
|
||||
"job_mem_limit"
|
||||
],
|
||||
"use_sudo": false
|
||||
}
|
||||
```
|
||||
|
||||
* The `cgroup_base` parameter (optional) can be set to specify the root path to SLURM job cgroups. The default is `/sys/fs/cgroup/system.slice/slurmstepd.scope`.
|
||||
* The `exclude_metrics` array can be used to suppress individual metrics from being sent to the sink.
|
||||
* The cgroups metrics are only available for root users. If password-less sudo is configured, you can enable sudo in the configuration.
|
||||
|
||||
### Reported metrics
|
||||
|
||||
All metrics are available **per hardware thread** :
|
||||
|
||||
* `job_mem_used` (`unit=Bytes`): Current memory usage of the job
|
||||
* `job_max_mem_used` (`unit=Bytes`): Peak memory usage
|
||||
* `job_mem_limit` (`unit=Bytes`): Cgroup memory limit
|
||||
* `job_user_cpu` (`unit=%`): User CPU utilization percentage
|
||||
* `job_sys_cpu` (`unit=%`): System CPU utilization percentage
|
||||
|
||||
Each metric has tags:
|
||||
|
||||
* `type=hwthread`
|
||||
* `type-id=<core_id>`
|
||||
|
||||
### Limitations
|
||||
|
||||
* **cgroups v2 required:** This collector only supports systems running with cgroups v2 (unified hierarchy).
|
@@ -4,7 +4,7 @@ The configuration of the CC metric collector consists of five configuration file
|
||||
|
||||
## Global configuration
|
||||
|
||||
The global file contains the paths to the other four files and some global options.
|
||||
The global file contains the paths to the other four files and some global options. You can find examples in `example_configs`.
|
||||
|
||||
```json
|
||||
{
|
||||
|
35
go.mod
35
go.mod
@@ -1,46 +1,45 @@
|
||||
module github.com/ClusterCockpit/cc-metric-collector
|
||||
|
||||
go 1.23.4
|
||||
|
||||
toolchain go1.23.7
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/ClusterCockpit/cc-lib v0.2.0
|
||||
github.com/ClusterCockpit/cc-lib v0.10.1
|
||||
github.com/ClusterCockpit/go-rocm-smi v0.3.0
|
||||
github.com/NVIDIA/go-nvml v0.12.4-1
|
||||
github.com/PaesslerAG/gval v1.2.2
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/NVIDIA/go-nvml v0.13.0-1
|
||||
github.com/PaesslerAG/gval v1.2.4
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf
|
||||
github.com/tklauser/go-sysconf v0.3.13
|
||||
github.com/tklauser/go-sysconf v0.3.15
|
||||
golang.design/x/thread v0.0.0-20210122121316-335e9adffdf1
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
|
||||
golang.org/x/sys v0.32.0
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
golang.org/x/sys v0.37.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/expr-lang/expr v1.17.2 // indirect
|
||||
github.com/expr-lang/expr v1.17.6 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0 // indirect
|
||||
github.com/influxdata/line-protocol/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nats-io/nats.go v1.41.2 // indirect
|
||||
github.com/nats-io/nats.go v1.46.1 // indirect
|
||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/oapi-codegen/runtime v1.1.1 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/stmcginnis/gofish v0.20.0 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
)
|
||||
|
82
go.sum
82
go.sum
@@ -1,15 +1,17 @@
|
||||
github.com/ClusterCockpit/cc-lib v0.2.0 h1:4mEpppqWXkjS3PCHYSOxF3NisrKsbPjg17ftsFhEKPk=
|
||||
github.com/ClusterCockpit/cc-lib v0.2.0/go.mod h1:S6oZNtejBHu+ewmGRKYFqJmlrRReXv8J4D2UP8hmu50=
|
||||
github.com/ClusterCockpit/cc-lib v0.10.1 h1:tjGEH8mFGgznYxO8BKLiiar0eZR1Oytk8x5iIQHZR5s=
|
||||
github.com/ClusterCockpit/cc-lib v0.10.1/go.mod h1:nvTZuxFCTwlos8I1rL5O1RPab7vRtkU8E/PGiaF6pQA=
|
||||
github.com/ClusterCockpit/go-rocm-smi v0.3.0 h1:1qZnSpG7/NyLtc7AjqnUL9Jb8xtqG1nMVgp69rJfaR8=
|
||||
github.com/ClusterCockpit/go-rocm-smi v0.3.0/go.mod h1:+I3UMeX3OlizXDf1WpGD43W4KGZZGVSGmny6rTeOnWA=
|
||||
github.com/NVIDIA/go-nvml v0.11.6-0/go.mod h1:hy7HYeQy335x6nEss0Ne3PYqleRa6Ct+VKD9RQ4nyFs=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-1 h1:WKUvqshhWSNTfm47ETRhv0A0zJyr1ncCuHiXwoTrBEc=
|
||||
github.com/NVIDIA/go-nvml v0.12.4-1/go.mod h1:8Llmj+1Rr+9VGGwZuRer5N/aCjxGuR5nPb/9ebBiIEQ=
|
||||
github.com/PaesslerAG/gval v1.2.2 h1:Y7iBzhgE09IGTt5QgGQ2IdaYYYOU134YGHBThD+wm9E=
|
||||
github.com/PaesslerAG/gval v1.2.2/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac=
|
||||
github.com/NVIDIA/go-nvml v0.13.0-1 h1:OLX8Jq3dONuPOQPC7rndB6+iDmDakw0XTYgzMxObkEw=
|
||||
github.com/NVIDIA/go-nvml v0.13.0-1/go.mod h1:+KNA7c7gIBH7SKSJ1ntlwkfN80zdx8ovl4hrK3LmPt4=
|
||||
github.com/PaesslerAG/gval v1.2.4 h1:rhX7MpjJlcxYwL2eTTYIOBUyEKZ+A96T9vQySWkVUiU=
|
||||
github.com/PaesslerAG/gval v1.2.4/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac=
|
||||
github.com/PaesslerAG/jsonpath v0.1.0 h1:gADYeifvlqK3R3i2cR5B4DGgxLXIPb3TRTH1mGi0jPI=
|
||||
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op h1:+OSa/t11TFhqfrX0EOSqQBDJ0YlpmK0rDSiB19dg9M0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -21,18 +23,20 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/expr-lang/expr v1.17.2 h1:o0A99O/Px+/DTjEnQiodAgOIK9PPxL8DtXhBRKC+Iso=
|
||||
github.com/expr-lang/expr v1.17.2/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec=
|
||||
github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/frankban/quicktest v1.11.0/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
|
||||
github.com/frankban/quicktest v1.11.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
|
||||
github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk=
|
||||
github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
|
||||
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
@@ -60,10 +64,16 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
|
||||
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nats-io/nats.go v1.41.2 h1:5UkfLAtu/036s99AhFRlyNDI1Ieylb36qbGjJzHixos=
|
||||
github.com/nats-io/nats.go v1.41.2/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/jwt/v2 v2.8.0 h1:K7uzyz50+yGZDO5o772eRE7atlcSEENpL7P+b74JV1g=
|
||||
github.com/nats-io/jwt/v2 v2.8.0/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA=
|
||||
github.com/nats-io/nats-server/v2 v2.12.0 h1:OIwe8jZUqJFrh+hhiyKu8snNib66qsx806OslqJuo74=
|
||||
github.com/nats-io/nats-server/v2 v2.12.0/go.mod h1:nr8dhzqkP5E/lDwmn+A2CvQPMd1yDKXQI7iGg3lAvww=
|
||||
github.com/nats-io/nats.go v1.46.1 h1:bqQ2ZcxVd2lpYI97xYASeRTY3I5boe/IVmuUDPitHfo=
|
||||
github.com/nats-io/nats.go v1.46.1/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
@@ -73,12 +83,12 @@ github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmt
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
@@ -92,28 +102,36 @@ github.com/stmcginnis/gofish v0.20.0 h1:hH2V2Qe898F2wWT1loApnkDUrXXiLKqbSlMaH3Y1
|
||||
github.com/stmcginnis/gofish v0.20.0/go.mod h1:PzF5i8ecRG9A2ol8XT64npKUunyraJ+7t0kYMpQAtqU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
golang.design/x/thread v0.0.0-20210122121316-335e9adffdf1 h1:P7S/GeHBAFEZIYp0ePPs2kHXoazz8q2KsyxHyQVGCJg=
|
||||
golang.design/x/thread v0.0.0-20210122121316-335e9adffdf1/go.mod h1:9CWpnTUmlQkfdpdutA1nNf4iE5lAVt3QZOu0Z6hahBE=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sys v0.0.0-20210122093101-04d7465088b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@@ -1,4 +1,6 @@
|
||||
Package: cc-metric-collector
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Version: {VERSION}
|
||||
Installed-Size: {INSTALLED_SIZE}
|
||||
Architecture: {ARCH}
|
||||
|
@@ -44,6 +44,8 @@ def group_to_json(groupfile):
|
||||
scope = "socket"
|
||||
if "PWR" in calc:
|
||||
scope = "socket"
|
||||
if "UMC" in calc:
|
||||
scope = "socket"
|
||||
|
||||
m = {"name" : metric, "calc": calc, "type" : scope, "publish" : True}
|
||||
metrics.append(m)
|
||||
|
Reference in New Issue
Block a user