Run ipmitool asynchron. Improved error handling.

This commit is contained in:
Holger Obermaier 2022-11-11 16:16:14 +01:00
parent 76bb033a88
commit 9e63d0ea59

View File

@ -1,51 +1,58 @@
package collectors package collectors
import ( import (
"bufio"
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io"
"log" "log"
"os"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
"time" "time"
cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger"
lp "github.com/ClusterCockpit/cc-metric-collector/pkg/ccMetric" lp "github.com/ClusterCockpit/cc-metric-collector/pkg/ccMetric"
) )
const IPMITOOL_PATH = `ipmitool`
const IPMISENSORS_PATH = `ipmi-sensors` const IPMISENSORS_PATH = `ipmi-sensors`
type IpmiCollectorConfig struct { type IpmiCollector struct {
metricCollector
config struct {
ExcludeDevices []string `json:"exclude_devices"` ExcludeDevices []string `json:"exclude_devices"`
IpmitoolPath string `json:"ipmitool_path"` IpmitoolPath string `json:"ipmitool_path"`
IpmisensorsPath string `json:"ipmisensors_path"` IpmisensorsPath string `json:"ipmisensors_path"`
} }
type IpmiCollector struct {
metricCollector
//tags map[string]string
//matches map[string]string
config IpmiCollectorConfig
ipmitool string ipmitool string
ipmisensors string ipmisensors string
} }
func (m *IpmiCollector) Init(config json.RawMessage) error { func (m *IpmiCollector) Init(config json.RawMessage) error {
// Check if already initialized
if m.init {
return nil
}
m.name = "IpmiCollector" m.name = "IpmiCollector"
m.setup() m.setup()
m.parallel = true m.parallel = true
m.meta = map[string]string{"source": m.name, "group": "IPMI"} m.meta = map[string]string{
m.config.IpmitoolPath = string(IPMITOOL_PATH) "source": m.name,
m.config.IpmisensorsPath = string(IPMISENSORS_PATH) "group": "IPMI",
m.ipmitool = "" }
m.ipmisensors = "" // default path to IPMI tools
m.config.IpmitoolPath = "ipmitool"
m.config.IpmisensorsPath = "ipmi-sensors"
if len(config) > 0 { if len(config) > 0 {
err := json.Unmarshal(config, &m.config) err := json.Unmarshal(config, &m.config)
if err != nil { if err != nil {
return err return err
} }
} }
// Check if executables ipmitool or ipmisensors are found
p, err := exec.LookPath(m.config.IpmitoolPath) p, err := exec.LookPath(m.config.IpmitoolPath)
if err == nil { if err == nil {
m.ipmitool = p m.ipmitool = p
@ -62,25 +69,33 @@ func (m *IpmiCollector) Init(config json.RawMessage) error {
} }
func (m *IpmiCollector) readIpmiTool(cmd string, output chan lp.CCMetric) { func (m *IpmiCollector) readIpmiTool(cmd string, output chan lp.CCMetric) {
// Setup ipmitool command
command := exec.Command(cmd, "sensor") command := exec.Command(cmd, "sensor")
command.Wait() stdout, _ := command.StdoutPipe()
stdout, err := command.Output() errBuf := new(bytes.Buffer)
if err != nil { command.Stderr = errBuf
log.Print(err)
// start command
if err := command.Start(); err != nil {
cclog.ComponentError(
m.name,
fmt.Sprintf("readIpmiTool(): Failed to start command \"%s\": %v", command.String(), err),
)
return return
} }
ll := strings.Split(string(stdout), "\n") // Read command output
scanner := bufio.NewScanner(stdout)
for _, line := range ll { for scanner.Scan() {
lv := strings.Split(line, "|") lv := strings.Split(scanner.Text(), "|")
if len(lv) < 3 { if len(lv) < 3 {
continue continue
} }
v, err := strconv.ParseFloat(strings.Trim(lv[1], " "), 64) v, err := strconv.ParseFloat(strings.TrimSpace(lv[1]), 64)
if err == nil { if err == nil {
name := strings.ToLower(strings.Replace(strings.Trim(lv[0], " "), " ", "_", -1)) name := strings.ToLower(strings.Replace(strings.TrimSpace(lv[0]), " ", "_", -1))
unit := strings.Trim(lv[2], " ") unit := strings.TrimSpace(lv[2])
if unit == "Volts" { if unit == "Volts" {
unit = "Volts" unit = "Volts"
} else if unit == "degrees C" { } else if unit == "degrees C" {
@ -98,6 +113,17 @@ func (m *IpmiCollector) readIpmiTool(cmd string, output chan lp.CCMetric) {
} }
} }
} }
// Wait for command end
if err := command.Wait(); err != nil {
errMsg, _ := io.ReadAll(errBuf)
cclog.ComponentError(
m.name,
fmt.Sprintf("readIpmiTool(): Failed to wait for the end of command \"%s\": %v\n", command.String(), err),
fmt.Sprintf("readIpmiTool(): command stderr: \"%s\"\n", string(errMsg)),
)
return
}
} }
func (m *IpmiCollector) readIpmiSensors(cmd string, output chan lp.CCMetric) { func (m *IpmiCollector) readIpmiSensors(cmd string, output chan lp.CCMetric) {
@ -131,18 +157,18 @@ func (m *IpmiCollector) readIpmiSensors(cmd string, output chan lp.CCMetric) {
} }
func (m *IpmiCollector) Read(interval time.Duration, output chan lp.CCMetric) { func (m *IpmiCollector) Read(interval time.Duration, output chan lp.CCMetric) {
// Check if already initialized
if !m.init {
return
}
if len(m.config.IpmitoolPath) > 0 { if len(m.config.IpmitoolPath) > 0 {
_, err := os.Stat(m.config.IpmitoolPath)
if err == nil {
m.readIpmiTool(m.config.IpmitoolPath, output) m.readIpmiTool(m.config.IpmitoolPath, output)
}
} else if len(m.config.IpmisensorsPath) > 0 { } else if len(m.config.IpmisensorsPath) > 0 {
_, err := os.Stat(m.config.IpmisensorsPath)
if err == nil {
m.readIpmiSensors(m.config.IpmisensorsPath, output) m.readIpmiSensors(m.config.IpmisensorsPath, output)
} }
} }
}
func (m *IpmiCollector) Close() { func (m *IpmiCollector) Close() {
m.init = false m.init = false