2021-03-25 15:55:06 +01:00
|
|
|
package collectors
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-01-27 11:08:27 +01:00
|
|
|
"os"
|
2022-01-26 20:18:47 +01:00
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric"
|
2022-01-27 11:08:27 +01:00
|
|
|
"golang.org/x/sys/unix"
|
2022-01-26 20:18:47 +01:00
|
|
|
|
2021-11-25 15:11:39 +01:00
|
|
|
"encoding/json"
|
2021-11-25 14:04:03 +01:00
|
|
|
"path/filepath"
|
2021-03-25 15:55:06 +01:00
|
|
|
"strconv"
|
2021-03-25 17:47:08 +01:00
|
|
|
"strings"
|
2021-03-25 15:55:06 +01:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2022-01-26 20:18:47 +01:00
|
|
|
const IB_BASEPATH = `/sys/class/infiniband/`
|
2021-11-25 14:04:03 +01:00
|
|
|
|
2022-01-27 11:08:27 +01:00
|
|
|
type InfinibandCollectorInfo struct {
|
|
|
|
LID string // IB local Identifier (LID)
|
|
|
|
device string // IB device
|
|
|
|
port string // IB device port
|
|
|
|
portCounterFiles map[string]string // mapping counter name -> file
|
|
|
|
tagSet map[string]string // corresponding tag list
|
|
|
|
}
|
|
|
|
|
2021-03-25 15:55:06 +01:00
|
|
|
type InfinibandCollector struct {
|
2022-01-25 15:37:43 +01:00
|
|
|
metricCollector
|
2022-01-26 20:18:47 +01:00
|
|
|
config struct {
|
2022-01-27 11:08:27 +01:00
|
|
|
ExcludeDevices []string `json:"exclude_devices,omitempty"` // IB device to exclude e.g. mlx5_0
|
2022-01-26 20:18:47 +01:00
|
|
|
}
|
2022-01-27 11:08:27 +01:00
|
|
|
info []InfinibandCollectorInfo
|
2021-03-25 15:55:06 +01:00
|
|
|
}
|
|
|
|
|
2021-11-25 16:25:20 +01:00
|
|
|
func (m *InfinibandCollector) Help() {
|
2022-01-26 20:18:47 +01:00
|
|
|
fmt.Println("This collector includes all devices that can be found below ", IB_BASEPATH)
|
|
|
|
fmt.Println("and where any of the ports provides a 'lid' file (glob ", IB_BASEPATH, "/<dev>/ports/<port>/lid).")
|
2021-11-25 16:25:20 +01:00
|
|
|
fmt.Println("The devices can be filtered with the 'exclude_devices' option in the configuration.")
|
|
|
|
fmt.Println("For each found LIDs the collector calls the 'perfquery' command")
|
2022-01-25 10:32:08 +01:00
|
|
|
fmt.Println("")
|
2021-11-25 16:25:20 +01:00
|
|
|
fmt.Println("Full configuration object:")
|
|
|
|
fmt.Println("\"ibstat\" : {")
|
|
|
|
fmt.Println(" \"exclude_devices\" : [\"dev1\"]")
|
2022-01-25 10:32:08 +01:00
|
|
|
fmt.Println("}")
|
|
|
|
fmt.Println("")
|
2021-11-25 16:25:20 +01:00
|
|
|
fmt.Println("Metrics:")
|
|
|
|
fmt.Println("- ib_recv")
|
|
|
|
fmt.Println("- ib_xmit")
|
2022-01-19 10:15:41 +01:00
|
|
|
fmt.Println("- ib_recv_pkts")
|
|
|
|
fmt.Println("- ib_xmit_pkts")
|
2021-11-25 16:25:20 +01:00
|
|
|
}
|
|
|
|
|
2022-01-27 11:08:27 +01:00
|
|
|
// Init initializes the Infiniband collector by walking through files below IB_BASEPATH
|
2022-01-25 15:37:43 +01:00
|
|
|
func (m *InfinibandCollector) Init(config json.RawMessage) error {
|
2021-11-25 15:11:39 +01:00
|
|
|
var err error
|
2021-03-25 17:47:08 +01:00
|
|
|
m.name = "InfinibandCollector"
|
2021-03-25 15:55:06 +01:00
|
|
|
m.setup()
|
2022-01-27 11:08:27 +01:00
|
|
|
m.meta = map[string]string{
|
|
|
|
"source": m.name,
|
|
|
|
"group": "Network",
|
|
|
|
}
|
2021-11-25 14:04:03 +01:00
|
|
|
if len(config) > 0 {
|
2021-11-25 15:11:39 +01:00
|
|
|
err = json.Unmarshal(config, &m.config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2022-01-27 11:08:27 +01:00
|
|
|
|
|
|
|
// Loop for all InfiniBand directories
|
|
|
|
globPattern := filepath.Join(IB_BASEPATH, "*", "ports", "*")
|
|
|
|
ibDirs, err := filepath.Glob(globPattern)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Unable to glob files with pattern %s: %v", globPattern, err)
|
|
|
|
}
|
|
|
|
if ibDirs == nil {
|
|
|
|
return fmt.Errorf("Unable to find any directories with pattern %s", globPattern)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, path := range ibDirs {
|
|
|
|
|
|
|
|
// Skip, when no LID is assigned
|
|
|
|
LID, ok := readOneLine(path + "/lid")
|
|
|
|
if !ok || LID == "0x0" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get device and port component
|
|
|
|
pathSplit := strings.Split(path, string(os.PathSeparator))
|
|
|
|
device := pathSplit[4]
|
|
|
|
port := pathSplit[6]
|
|
|
|
|
|
|
|
// Skip excluded devices
|
|
|
|
skip := false
|
|
|
|
for _, excludedDevice := range m.config.ExcludeDevices {
|
|
|
|
if excludedDevice == device {
|
|
|
|
skip = true
|
|
|
|
break
|
2021-11-25 15:11:39 +01:00
|
|
|
}
|
2022-01-27 11:08:27 +01:00
|
|
|
}
|
|
|
|
if skip {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check access to counter files
|
|
|
|
countersDir := filepath.Join(path, "counters")
|
|
|
|
portCounterFiles := map[string]string{
|
|
|
|
"ib_recv": filepath.Join(countersDir, "port_rcv_data"),
|
|
|
|
"ib_xmit": filepath.Join(countersDir, "port_xmit_data"),
|
|
|
|
"ib_recv_pkts": filepath.Join(countersDir, "port_rcv_packets"),
|
|
|
|
"ib_xmit_pkts": filepath.Join(countersDir, "port_xmit_packets"),
|
|
|
|
}
|
|
|
|
for _, counterFile := range portCounterFiles {
|
|
|
|
err := unix.Access(counterFile, unix.R_OK)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Unable to access %s: %v", counterFile, err)
|
2021-11-25 15:11:39 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-27 11:08:27 +01:00
|
|
|
|
|
|
|
m.info = append(m.info,
|
|
|
|
InfinibandCollectorInfo{
|
|
|
|
LID: LID,
|
|
|
|
device: device,
|
|
|
|
port: port,
|
|
|
|
portCounterFiles: portCounterFiles,
|
|
|
|
tagSet: map[string]string{
|
|
|
|
"type": "node",
|
|
|
|
"device": device,
|
|
|
|
"port": port,
|
|
|
|
"lid": LID,
|
|
|
|
},
|
|
|
|
})
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2021-11-25 15:11:39 +01:00
|
|
|
|
2022-01-27 11:08:27 +01:00
|
|
|
if len(m.info) == 0 {
|
|
|
|
return fmt.Errorf("Found no IB devices")
|
2021-03-25 15:55:06 +01:00
|
|
|
}
|
2021-03-25 17:47:08 +01:00
|
|
|
|
2022-01-26 20:18:47 +01:00
|
|
|
m.init = true
|
|
|
|
return nil
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2021-03-25 17:47:08 +01:00
|
|
|
|
2022-01-27 11:08:27 +01:00
|
|
|
// Read reads Infiniband counter files below IB_BASEPATH
|
2022-01-26 20:18:47 +01:00
|
|
|
func (m *InfinibandCollector) Read(interval time.Duration, output chan lp.CCMetric) {
|
2021-03-25 15:55:06 +01:00
|
|
|
|
2022-01-27 11:08:27 +01:00
|
|
|
// Check if already initialized
|
|
|
|
if !m.init {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
for i := range m.info {
|
|
|
|
|
|
|
|
// device info
|
|
|
|
info := &m.info[i]
|
|
|
|
for counterName, counterFile := range info.portCounterFiles {
|
|
|
|
if data, ok := readOneLine(counterFile); ok {
|
|
|
|
if v, err := strconv.ParseInt(data, 10, 64); err == nil {
|
|
|
|
if y, err := lp.New(counterName, info.tagSet, m.meta, map[string]interface{}{"value": v}, now); err == nil {
|
|
|
|
output <- y
|
2022-01-26 20:18:47 +01:00
|
|
|
}
|
2021-11-25 15:11:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 15:55:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *InfinibandCollector) Close() {
|
2021-10-04 15:47:03 +02:00
|
|
|
m.init = false
|
2021-03-25 15:55:06 +01:00
|
|
|
}
|