mirror of
https://github.com/ClusterCockpit/cc-metric-collector.git
synced 2026-02-26 12:47:29 +01:00
We should not rely on the availablity of certain nfs metrics at just the collector start time. Allow new metrics to come in at any point.
193 lines
4.0 KiB
Go
193 lines
4.0 KiB
Go
// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
|
|
// All rights reserved. This file is part of cc-lib.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
// additional authors:
|
|
// Holger Obermaier (NHR@KIT)
|
|
|
|
package collectors
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
|
|
// "os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
cclog "github.com/ClusterCockpit/cc-lib/v2/ccLogger"
|
|
lp "github.com/ClusterCockpit/cc-lib/v2/ccMessage"
|
|
)
|
|
|
|
// First part contains the code for the general NfsCollector.
|
|
// Later, the general NfsCollector is more limited to Nfs3- and Nfs4Collector.
|
|
|
|
const NFSSTAT_EXEC = `nfsstat`
|
|
|
|
type NfsCollectorData struct {
|
|
current int64
|
|
last int64
|
|
}
|
|
|
|
type nfsCollector struct {
|
|
metricCollector
|
|
|
|
tags map[string]string
|
|
version string
|
|
config struct {
|
|
Nfsstats string `json:"nfsstat"`
|
|
ExcludeMetrics []string `json:"exclude_metrics,omitempty"`
|
|
}
|
|
data map[string]NfsCollectorData
|
|
}
|
|
|
|
func (m *nfsCollector) updateStats() error {
|
|
cmd := exec.Command(m.config.Nfsstats, `-l`, `--all`)
|
|
|
|
// Wait for cmd end
|
|
if err := cmd.Wait(); err != nil {
|
|
return fmt.Errorf("%s updateStats(): %w", m.name, err)
|
|
}
|
|
|
|
buffer, err := cmd.Output()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for name, data := range m.data {
|
|
m.data[name] = NfsCollectorData{
|
|
last: data.current,
|
|
current: -1,
|
|
}
|
|
}
|
|
|
|
for line := range strings.Lines(string(buffer)) {
|
|
lf := strings.Fields(line)
|
|
if len(lf) != 5 {
|
|
continue
|
|
}
|
|
if lf[1] != m.version {
|
|
continue
|
|
}
|
|
name := strings.Trim(lf[3], ":")
|
|
value, err := strconv.ParseInt(lf[4], 0, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
collectorData, exist := m.data[name]
|
|
collectorData.current = value
|
|
|
|
if !exist {
|
|
collectorData.last = -1
|
|
}
|
|
|
|
m.data[name] = collectorData
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *nfsCollector) MainInit(config json.RawMessage) error {
|
|
m.config.Nfsstats = string(NFSSTAT_EXEC)
|
|
// Read JSON configuration
|
|
if len(config) > 0 {
|
|
err := json.Unmarshal(config, &m.config)
|
|
if err != nil {
|
|
return fmt.Errorf("%s Init(): failed to unmarshal JSON config: %w", m.name, err)
|
|
}
|
|
}
|
|
m.meta = map[string]string{
|
|
"source": m.name,
|
|
"group": "NFS",
|
|
}
|
|
m.tags = map[string]string{
|
|
"type": "node",
|
|
}
|
|
// Check if nfsstat is in executable search path
|
|
_, err := exec.LookPath(m.config.Nfsstats)
|
|
if err != nil {
|
|
return fmt.Errorf("%s Init(): Failed to find nfsstat binary '%s': %w", m.name, m.config.Nfsstats, err)
|
|
}
|
|
m.data = make(map[string]NfsCollectorData)
|
|
if err := m.updateStats(); err != nil {
|
|
return fmt.Errorf("%s Init(): %w", m.name, err)
|
|
}
|
|
m.init = true
|
|
m.parallel = true
|
|
return nil
|
|
}
|
|
|
|
func (m *nfsCollector) Read(interval time.Duration, output chan lp.CCMessage) {
|
|
if !m.init {
|
|
return
|
|
}
|
|
timestamp := time.Now()
|
|
|
|
if err := m.updateStats(); err != nil {
|
|
cclog.ComponentError(
|
|
m.name,
|
|
fmt.Sprintf("Read(): updateStats() failed: %v", err),
|
|
)
|
|
return
|
|
}
|
|
var prefix string
|
|
switch m.version {
|
|
case "v3":
|
|
prefix = "nfs3"
|
|
case "v4":
|
|
prefix = "nfs4"
|
|
default:
|
|
prefix = "nfs"
|
|
}
|
|
|
|
for name, data := range m.data {
|
|
if slices.Contains(m.config.ExcludeMetrics, name) {
|
|
continue
|
|
}
|
|
|
|
valueMap := make(map[string]any)
|
|
if data.current >= 0 && data.last >= 0 {
|
|
valueMap["value"] = data.current - data.last
|
|
}
|
|
y, err := lp.NewMessage(fmt.Sprintf("%s_%s", prefix, name), m.tags, m.meta, valueMap, timestamp)
|
|
if err == nil {
|
|
y.AddMeta("version", m.version)
|
|
output <- y
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *nfsCollector) Close() {
|
|
m.init = false
|
|
}
|
|
|
|
type Nfs3Collector struct {
|
|
nfsCollector
|
|
}
|
|
|
|
type Nfs4Collector struct {
|
|
nfsCollector
|
|
}
|
|
|
|
func (m *Nfs3Collector) Init(config json.RawMessage) error {
|
|
m.name = "Nfs3Collector"
|
|
m.version = `v3`
|
|
if err := m.setup(); err != nil {
|
|
return fmt.Errorf("%s Init(): setup() call failed: %w", m.name, err)
|
|
}
|
|
return m.MainInit(config)
|
|
}
|
|
|
|
func (m *Nfs4Collector) Init(config json.RawMessage) error {
|
|
m.name = "Nfs4Collector"
|
|
m.version = `v4`
|
|
if err := m.setup(); err != nil {
|
|
return fmt.Errorf("%s Init(): setup() call failed: %w", m.name, err)
|
|
}
|
|
return m.MainInit(config)
|
|
}
|