Add collector to read data from ipmitool or ipmi-sensors (fallback) (#9)

* Add collector to read data from ipmitool or ipmi-sensors

* Update IPMI collector to use own config

* Add 'ipmitool sensor' parser
This commit is contained in:
Thomas Gruber 2021-11-25 18:15:56 +01:00 committed by GitHub
parent c6edf7aed4
commit 1e7a75598e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 139 additions and 0 deletions

View File

@ -40,6 +40,7 @@ The base class/configuration is located in `metricCollector.go`.
* `cpustatMetric.go`: Read CPU specific values from `/proc/stat`
* `topprocsMetric.go`: Reads the Top5 processes by their CPU usage
* `nvidiaMetric.go`: Read data about Nvidia GPUs using the NVML library
* `ipmiMetric.go`: Collect data from `ipmitool` or as fallback `ipmi-sensors`
If any of the collectors cannot be initialized, it is excluded from all further reads. Like if the Lustre stat file is not a valid path, no Lustre specific metrics will be recorded.

137
collectors/ipmiMetric.go Normal file
View File

@ -0,0 +1,137 @@
package collectors
import (
"errors"
lp "github.com/influxdata/line-protocol"
"log"
"strconv"
"strings"
"time"
"os"
"os/exec"
"encoding/json"
)
const IPMITOOL_PATH = `/usr/bin/ipmitool`
const IPMISENSORS_PATH = `/usr/sbin/ipmi-sensors`
type IpmiCollectorConfig struct {
ExcludeDevices []string `json:"exclude_devices"`
IpmitoolPath string `json:"ipmitool_path"`
IpmisensorsPath string `json:"ipmisensors_path"`
}
type IpmiCollector struct {
MetricCollector
tags map[string]string
matches map[string]string
config IpmiCollectorConfig
}
func (m *IpmiCollector) Init(config []byte) error {
m.name = "IpmiCollector"
m.setup()
if len(config) > 0 {
err := json.Unmarshal(config, &m.config)
if err != nil {
return err
}
}
_, err1 := os.Stat(m.config.IpmitoolPath)
_, err2 := os.Stat(m.config.IpmisensorsPath)
if err1 != nil {
m.config.IpmitoolPath = ""
}
if err2 != nil {
m.config.IpmisensorsPath = ""
}
if err1 != nil && err2 != nil {
return errors.New("No IPMI reader found")
}
m.init = true
return nil
}
func ReadIpmiTool(cmd string, out *[]lp.MutableMetric) {
command := exec.Command(cmd, "sensor")
command.Wait()
stdout, err := command.Output()
if err != nil {
log.Print(err)
return
}
ll := strings.Split(string(stdout), "\n")
for _, line := range ll {
lv := strings.Split(line, "|")
if len(lv) < 3 {
continue
}
v, err := strconv.ParseFloat(strings.Trim(lv[1], " "), 64)
if err == nil {
name := strings.ToLower(strings.Replace(strings.Trim(lv[0], " "), " ", "_", -1))
unit := strings.Trim(lv[2], " ")
if unit == "Volts" {
unit = "V"
} else if unit == "degrees C" {
unit = "C"
} else if unit == "degrees F" {
unit = "F"
} else if unit == "Watts" {
unit = "W"
}
y, err := lp.New(name, map[string]string{"unit": unit, "type" : "node"}, map[string]interface{}{"value": v}, time.Now())
if err == nil {
*out = append(*out, y)
}
}
}
}
func ReadIpmiSensors(cmd string, out *[]lp.MutableMetric) {
command := exec.Command(cmd, "--comma-separated-output", "--sdr-cache-recreate")
command.Wait()
stdout, err := command.Output()
if err != nil {
log.Print(err)
return
}
ll := strings.Split(string(stdout), "\n")
for _, line := range ll {
lv := strings.Split(line, ",")
if len(lv) > 3 {
v, err := strconv.ParseFloat(lv[3], 64)
if err == nil {
name := strings.ToLower(strings.Replace(lv[1], " ", "_", -1))
y, err := lp.New(name, map[string]string{"unit": lv[4], "type" : "node"}, map[string]interface{}{"value": v}, time.Now())
if err == nil {
*out = append(*out, y)
}
}
}
}
}
func (m *IpmiCollector) Read(interval time.Duration, out *[]lp.MutableMetric) {
if len(m.config.IpmitoolPath) > 0 {
_, err := os.Stat(m.config.IpmitoolPath)
if err == nil {
ReadIpmiTool(m.config.IpmitoolPath, out)
}
} else if len(m.config.IpmisensorsPath) > 0 {
_, err := os.Stat(m.config.IpmisensorsPath)
if err == nil {
ReadIpmiSensors(m.config.IpmisensorsPath, out)
}
}
}
func (m *IpmiCollector) Close() {
m.init = false
return
}

View File

@ -30,6 +30,7 @@ var Collectors = map[string]collectors.MetricGetter{
"nvidia": &collectors.NvidiaCollector{},
"customcmd": &collectors.CustomCmdCollector{},
"diskstat": &collectors.DiskstatCollector{},
"ipmistat" : &collectors.IpmiCollector{},
}
var Sinks = map[string]sinks.SinkFuncs{