mirror of
https://github.com/ClusterCockpit/cc-metric-collector.git
synced 2024-11-10 12:37:25 +01:00
191 lines
4.8 KiB
Go
191 lines
4.8 KiB
Go
|
package collectors
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os/exec"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
const LUSTRE_SYSFS = `/sys/fs/lustre`
|
||
|
const LCTL_CMD = `lctl`
|
||
|
const LCTL_OPTION = `get_param`
|
||
|
|
||
|
type LustreMetricDefinition struct {
|
||
|
name string
|
||
|
lineprefix string
|
||
|
lineoffset int
|
||
|
offsetname string
|
||
|
unit string
|
||
|
calc string
|
||
|
}
|
||
|
|
||
|
type LustreMetricData struct {
|
||
|
sample_time int64
|
||
|
start_time int64
|
||
|
elapsed_time int64
|
||
|
op_data map[string]map[string]int64
|
||
|
op_units map[string]string
|
||
|
sample_time_unit string
|
||
|
start_time_unit string
|
||
|
elapsed_time_unit string
|
||
|
}
|
||
|
|
||
|
var devicePattern = regexp.MustCompile(`^[\w\d\-_]+\.([\w\d\-_]+)\.[\w\d\-_]+=$`)
|
||
|
var jobPattern = regexp.MustCompile(`^-\s*job_id:\s*([\w\d\-_\.:]+)$`)
|
||
|
var snapshotPattern = regexp.MustCompile(`^\s*snapshot_time\s*:\s*([\d\.]+)\s*([\w\d\-_\.]*)$`)
|
||
|
var startPattern = regexp.MustCompile(`^\s*start_time\s*:\s*([\d\.]+)\s*([\w\d\-_\.]*)$`)
|
||
|
var elapsedPattern = regexp.MustCompile(`^\s*elapsed_time\s*:\s*([\d\.]+)\s*([\w\d\-_\.]*)$`)
|
||
|
var linePattern = regexp.MustCompile(`^\s*([\w\d\-_\.]+):\s*\{\s*samples:\s*([\d\.]+),\s*unit:\s*([\w\d\-_\.]+),\s*min:\s*([\d\.]+),\s*max:\s*([\d\.]+),\s*sum:\s*([\d\.]+),\s*sumsq:\s*([\d\.]+)\s*\}`)
|
||
|
|
||
|
func executeLustreCommand(sudo, lctl, option, search string, use_sudo bool) []string {
|
||
|
var command *exec.Cmd
|
||
|
if use_sudo {
|
||
|
command = exec.Command(sudo, lctl, option, search)
|
||
|
} else {
|
||
|
command = exec.Command(lctl, option, search)
|
||
|
}
|
||
|
command.Wait()
|
||
|
stdout, _ := command.Output()
|
||
|
return strings.Split(string(stdout), "\n")
|
||
|
}
|
||
|
|
||
|
func splitTree(lines []string, splitRegex *regexp.Regexp) map[string][]string {
|
||
|
entries := make(map[string][]string)
|
||
|
ent_lines := make([]int, 0)
|
||
|
for i, l := range lines {
|
||
|
m := splitRegex.FindStringSubmatch(l)
|
||
|
if len(m) == 2 {
|
||
|
ent_lines = append(ent_lines, i)
|
||
|
}
|
||
|
}
|
||
|
if len(ent_lines) > 0 {
|
||
|
for i, idx := range ent_lines[:len(ent_lines)-1] {
|
||
|
m := splitRegex.FindStringSubmatch(lines[idx])
|
||
|
entries[m[1]] = lines[idx+1 : ent_lines[i+1]]
|
||
|
}
|
||
|
last := ent_lines[len(ent_lines)-1]
|
||
|
m := splitRegex.FindStringSubmatch(lines[last])
|
||
|
entries[m[1]] = lines[last:]
|
||
|
}
|
||
|
return entries
|
||
|
}
|
||
|
|
||
|
func readDevices(lines []string) map[string][]string {
|
||
|
return splitTree(lines, devicePattern)
|
||
|
}
|
||
|
|
||
|
func readJobs(lines []string) map[string][]string {
|
||
|
return splitTree(lines, jobPattern)
|
||
|
}
|
||
|
|
||
|
func readJobdata(lines []string) LustreMetricData {
|
||
|
|
||
|
jobdata := LustreMetricData{
|
||
|
op_data: make(map[string]map[string]int64),
|
||
|
op_units: make(map[string]string),
|
||
|
sample_time: 0,
|
||
|
sample_time_unit: "nsec",
|
||
|
start_time: 0,
|
||
|
start_time_unit: "nsec",
|
||
|
elapsed_time: 0,
|
||
|
elapsed_time_unit: "nsec",
|
||
|
}
|
||
|
parseTime := func(value, unit string) int64 {
|
||
|
var t int64 = 0
|
||
|
if len(unit) == 0 {
|
||
|
unit = "secs"
|
||
|
}
|
||
|
values := strings.Split(value, ".")
|
||
|
units := strings.Split(unit, ".")
|
||
|
if len(values) != len(units) {
|
||
|
fmt.Printf("Invalid time specification '%s' and '%s'\n", value, unit)
|
||
|
}
|
||
|
for i, v := range values {
|
||
|
if len(units) > i {
|
||
|
s, err := strconv.ParseInt(v, 10, 64)
|
||
|
if err == nil {
|
||
|
switch units[i] {
|
||
|
case "secs":
|
||
|
t += s * 1e9
|
||
|
case "msecs":
|
||
|
t += s * 1e6
|
||
|
case "usecs":
|
||
|
t += s * 1e3
|
||
|
case "nsecs":
|
||
|
t += s
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return t
|
||
|
}
|
||
|
parseNumber := func(value string) int64 {
|
||
|
s, err := strconv.ParseInt(value, 10, 64)
|
||
|
if err == nil {
|
||
|
return s
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
for _, l := range lines {
|
||
|
if jobdata.sample_time == 0 {
|
||
|
m := snapshotPattern.FindStringSubmatch(l)
|
||
|
if len(m) == 3 {
|
||
|
if len(m[2]) > 0 {
|
||
|
jobdata.sample_time = parseTime(m[1], m[2])
|
||
|
} else {
|
||
|
jobdata.sample_time = parseTime(m[1], "secs")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if jobdata.start_time == 0 {
|
||
|
m := startPattern.FindStringSubmatch(l)
|
||
|
if len(m) == 3 {
|
||
|
if len(m[2]) > 0 {
|
||
|
jobdata.start_time = parseTime(m[1], m[2])
|
||
|
} else {
|
||
|
jobdata.start_time = parseTime(m[1], "secs")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if jobdata.elapsed_time == 0 {
|
||
|
m := elapsedPattern.FindStringSubmatch(l)
|
||
|
if len(m) == 3 {
|
||
|
if len(m[2]) > 0 {
|
||
|
jobdata.elapsed_time = parseTime(m[1], m[2])
|
||
|
} else {
|
||
|
jobdata.elapsed_time = parseTime(m[1], "secs")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
m := linePattern.FindStringSubmatch(l)
|
||
|
if len(m) == 8 {
|
||
|
jobdata.op_units[m[1]] = m[3]
|
||
|
jobdata.op_data[m[1]] = map[string]int64{
|
||
|
"samples": parseNumber(m[2]),
|
||
|
"min": parseNumber(m[4]),
|
||
|
"max": parseNumber(m[5]),
|
||
|
"sum": parseNumber(m[6]),
|
||
|
"sumsq": parseNumber(m[7]),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return jobdata
|
||
|
}
|
||
|
|
||
|
func readCommandOutput(lines []string) map[string]map[string]LustreMetricData {
|
||
|
var data map[string]map[string]LustreMetricData = make(map[string]map[string]LustreMetricData)
|
||
|
devs := readDevices(lines)
|
||
|
for d, ddata := range devs {
|
||
|
data[d] = make(map[string]LustreMetricData)
|
||
|
jobs := readJobs(ddata)
|
||
|
for j, jdata := range jobs {
|
||
|
x := readJobdata(jdata)
|
||
|
data[d][j] = x
|
||
|
}
|
||
|
}
|
||
|
return data
|
||
|
}
|