smb: refactor collector (#1740)

Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke 2024-11-15 21:27:28 +01:00 committed by GitHub
parent b209ab6d6a
commit 6206b695c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 147 additions and 136 deletions

View File

@ -0,0 +1,6 @@
package smb
const (
currentOpenFileCount = "Current Open File Count"
treeConnectCount = "Tree Connect Count"
)

View File

@ -3,12 +3,14 @@
package smb package smb
import ( import (
"errors"
"fmt"
"log/slog" "log/slog"
"strings"
"github.com/alecthomas/kingpin/v2" "github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1" "github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
@ -22,6 +24,8 @@ var ConfigDefaults = Config{}
type Collector struct { type Collector struct {
config Config config Config
perfDataCollector perfdata.Collector
treeConnectCount *prometheus.Desc treeConnectCount *prometheus.Desc
currentOpenFileCount *prometheus.Desc currentOpenFileCount *prometheus.Desc
} }
@ -47,89 +51,73 @@ func (c *Collector) GetName() string {
} }
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{ return []string{}, nil
"SMB Server Shares",
}, nil
} }
func (c *Collector) Close(_ *slog.Logger) error { func (c *Collector) Close(_ *slog.Logger) error {
c.perfDataCollector.Close()
return nil return nil
} }
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
// desc creates a new prometheus description var err error
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
return prometheus.NewDesc( c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "SMB Server Shares", nil, []string{
prometheus.BuildFQName(types.Namespace, "smb", metricName), currentOpenFileCount,
description, treeConnectCount,
labels, })
nil, if err != nil {
) return fmt.Errorf("failed to create SMB Server Shares collector: %w", err)
} }
c.currentOpenFileCount = desc("server_shares_current_open_file_count", "Current total count open files on the SMB Server") c.currentOpenFileCount = prometheus.NewDesc(
c.treeConnectCount = desc("server_shares_tree_connect_count", "Count of user connections to the SMB Server") prometheus.BuildFQName(types.Namespace, Name, "server_shares_current_open_file_count"),
"Current total count open files on the SMB Server",
nil,
nil,
)
c.treeConnectCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "server_shares_tree_connect_count"),
"Count of user connections to the SMB Server",
nil,
nil,
)
return nil return nil
} }
// Collect collects smb metrics and sends them to prometheus. // Collect collects smb metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name)) if err := c.collectServerShares(ch); err != nil {
if err := c.collectServerShares(ctx, logger, ch); err != nil { return fmt.Errorf("failed to collect server share metrics: %w", err)
logger.Error("failed to collect server share metrics",
slog.Any("err", err),
)
return err
} }
return nil return nil
} }
// Perflib: SMB Server Shares. func (c *Collector) collectServerShares(ch chan<- prometheus.Metric) error {
type perflibServerShares struct { perfData, err := c.perfDataCollector.Collect()
Name string if err != nil {
return fmt.Errorf("failed to collect SMB Server Shares metrics: %w", err)
CurrentOpenFileCount float64 `perflib:"Current Open File Count"`
TreeConnectCount float64 `perflib:"Tree Connect Count"`
} }
func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { data, ok := perfData[perftypes.EmptyInstance]
logger = logger.With(slog.String("collector", Name)) if !ok {
return errors.New("query for SMB Server Shares returned empty result set")
var data []perflibServerShares
if err := v1.UnmarshalObject(ctx.PerfObjects["SMB Server Shares"], &data, logger); err != nil {
return err
}
for _, instance := range data {
labelName := c.toLabelName(instance.Name)
if !strings.HasSuffix(labelName, "_total") {
continue
} }
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.currentOpenFileCount, c.currentOpenFileCount,
prometheus.CounterValue, prometheus.CounterValue,
instance.CurrentOpenFileCount, data[currentOpenFileCount].FirstValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.treeConnectCount, c.treeConnectCount,
prometheus.CounterValue, prometheus.CounterValue,
instance.TreeConnectCount, data[treeConnectCount].FirstValue,
) )
}
return nil return nil
} }
// toLabelName converts strings to lowercase and replaces all whitespaces and dots with underscores.
func (c *Collector) toLabelName(name string) string {
s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_")
s = strings.ReplaceAll(s, "__", "_")
return s
}

View File

@ -0,0 +1,25 @@
package smbclient
const (
AvgDataQueueLength = "Avg. Data Queue Length"
AvgReadQueueLength = "Avg. Read Queue Length"
AvgSecPerRead = "Avg. sec/Read"
AvgSecPerWrite = "Avg. sec/Write"
AvgSecPerDataRequest = "Avg. sec/Data Request"
AvgWriteQueueLength = "Avg. Write Queue Length"
CreditStallsPerSec = "Credit Stalls/sec"
CurrentDataQueueLength = "Current Data Queue Length"
DataBytesPerSec = "Data Bytes/sec"
DataRequestsPerSec = "Data Requests/sec"
MetadataRequestsPerSec = "Metadata Requests/sec"
ReadBytesTransmittedViaSMBDirectPerSec = "Read Bytes transmitted via SMB Direct/sec"
ReadBytesPerSec = "Read Bytes/sec"
ReadRequestsTransmittedViaSMBDirectPerSec = "Read Requests transmitted via SMB Direct/sec"
ReadRequestsPerSec = "Read Requests/sec"
TurboIOReadsPerSec = "Turbo I/O Reads/sec"
TurboIOWritesPerSec = "Turbo I/O Writes/sec"
WriteBytesTransmittedViaSMBDirectPerSec = "Write Bytes transmitted via SMB Direct/sec"
WriteBytesPerSec = "Write Bytes/sec"
WriteRequestsTransmittedViaSMBDirectPerSec = "Write Requests transmitted via SMB Direct/sec"
WriteRequestsPerSec = "Write Requests/sec"
)

View File

@ -3,13 +3,14 @@
package smbclient package smbclient
import ( import (
"fmt"
"log/slog" "log/slog"
"strings" "strings"
"github.com/alecthomas/kingpin/v2" "github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes" "github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
@ -25,6 +26,8 @@ var ConfigDefaults = Config{}
type Collector struct { type Collector struct {
config Config config Config
perfDataCollector perfdata.Collector
readBytesTotal *prometheus.Desc readBytesTotal *prometheus.Desc
readBytesTransmittedViaSMBDirectTotal *prometheus.Desc readBytesTransmittedViaSMBDirectTotal *prometheus.Desc
readRequestQueueSecsTotal *prometheus.Desc readRequestQueueSecsTotal *prometheus.Desc
@ -70,9 +73,7 @@ func (c *Collector) GetName() string {
} }
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{ return []string{}, nil
"SMB Client Shares",
}, nil
} }
func (c *Collector) Close(_ *slog.Logger) error { func (c *Collector) Close(_ *slog.Logger) error {
@ -80,10 +81,39 @@ func (c *Collector) Close(_ *slog.Logger) error {
} }
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error { func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
var err error
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "SMB Client Shares", nil, []string{
AvgDataQueueLength,
AvgReadQueueLength,
AvgSecPerRead,
AvgSecPerWrite,
AvgSecPerDataRequest,
AvgWriteQueueLength,
CreditStallsPerSec,
CurrentDataQueueLength,
DataBytesPerSec,
DataRequestsPerSec,
MetadataRequestsPerSec,
ReadBytesTransmittedViaSMBDirectPerSec,
ReadBytesPerSec,
ReadRequestsTransmittedViaSMBDirectPerSec,
ReadRequestsPerSec,
TurboIOReadsPerSec,
TurboIOWritesPerSec,
WriteBytesTransmittedViaSMBDirectPerSec,
WriteBytesPerSec,
WriteRequestsTransmittedViaSMBDirectPerSec,
WriteRequestsPerSec,
})
if err != nil {
return fmt.Errorf("failed to create SMB Client Shares collector: %w", err)
}
// desc creates a new prometheus description // desc creates a new prometheus description
desc := func(metricName string, description string, labels []string) *prometheus.Desc { desc := func(metricName string, description string, labels []string) *prometheus.Desc {
return prometheus.NewDesc( return prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "smbclient", metricName), prometheus.BuildFQName(types.Namespace, Name, metricName),
description, description,
labels, labels,
nil, nil,
@ -179,68 +209,30 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
} }
// Collect collects smb client metrics and sends them to prometheus. // Collect collects smb client metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name)) if err := c.collectClientShares(ch); err != nil {
if err := c.collectClientShares(ctx, logger, ch); err != nil {
logger.Error("Error in ClientShares",
slog.Any("err", err),
)
return err return err
} }
return nil return nil
} }
// Perflib: SMB Client Shares. func (c *Collector) collectClientShares(ch chan<- prometheus.Metric) error {
type perflibClientShares struct { perfData, err := c.perfDataCollector.Collect()
Name string if err != nil {
return fmt.Errorf("failed to collect SMB Client Shares metrics: %w", err)
AvgDataQueueLength float64 `perflib:"Avg. Data Queue Length"`
AvgReadQueueLength float64 `perflib:"Avg. Read Queue Length"`
AvgSecPerRead float64 `perflib:"Avg. sec/Read"`
AvgSecPerWrite float64 `perflib:"Avg. sec/Write"`
AvgSecPerDataRequest float64 `perflib:"Avg. sec/Data Request"`
AvgWriteQueueLength float64 `perflib:"Avg. Write Queue Length"`
CreditStallsPerSec float64 `perflib:"Credit Stalls/sec"`
CurrentDataQueueLength float64 `perflib:"Current Data Queue Length"`
DataBytesPerSec float64 `perflib:"Data Bytes/sec"`
DataRequestsPerSec float64 `perflib:"Data Requests/sec"`
MetadataRequestsPerSec float64 `perflib:"Metadata Requests/sec"`
ReadBytesTransmittedViaSMBDirectPerSec float64 `perflib:"Read Bytes transmitted via SMB Direct/sec"`
ReadBytesPerSec float64 `perflib:"Read Bytes/sec"`
ReadRequestsTransmittedViaSMBDirectPerSec float64 `perflib:"Read Requests transmitted via SMB Direct/sec"`
ReadRequestsPerSec float64 `perflib:"Read Requests/sec"`
TurboIOReadsPerSec float64 `perflib:"Turbo I/O Reads/sec"`
TurboIOWritesPerSec float64 `perflib:"Turbo I/O Writes/sec"`
WriteBytesTransmittedViaSMBDirectPerSec float64 `perflib:"Write Bytes transmitted via SMB Direct/sec"`
WriteBytesPerSec float64 `perflib:"Write Bytes/sec"`
WriteRequestsTransmittedViaSMBDirectPerSec float64 `perflib:"Write Requests transmitted via SMB Direct/sec"`
WriteRequestsPerSec float64 `perflib:"Write Requests/sec"`
} }
func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { for name, data := range perfData {
logger = logger.With(slog.String("collector", Name)) parsed := strings.FieldsFunc(name, func(r rune) bool { return r == '\\' })
var data []perflibClientShares
if err := v1.UnmarshalObject(ctx.PerfObjects["SMB Client Shares"], &data, logger); err != nil {
return err
}
for _, instance := range data {
if instance.Name == "_Total" {
continue
}
parsed := strings.FieldsFunc(instance.Name, func(r rune) bool { return r == '\\' })
serverValue := parsed[0] serverValue := parsed[0]
shareValue := parsed[1] shareValue := parsed[1]
// Request time spent on queue. Convert from ticks to seconds. // Request time spent on queue. Convert from ticks to seconds.
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.requestQueueSecsTotal, c.requestQueueSecsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.AvgDataQueueLength*perftypes.TicksToSecondScaleFactor, data[AvgDataQueueLength].FirstValue*perftypes.TicksToSecondScaleFactor,
serverValue, shareValue, serverValue, shareValue,
) )
@ -248,28 +240,28 @@ func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.L
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.readRequestQueueSecsTotal, c.readRequestQueueSecsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.AvgReadQueueLength*perftypes.TicksToSecondScaleFactor, data[AvgReadQueueLength].FirstValue*perftypes.TicksToSecondScaleFactor,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.readSecsTotal, c.readSecsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.AvgSecPerRead*perftypes.TicksToSecondScaleFactor, data[AvgSecPerRead].FirstValue*perftypes.TicksToSecondScaleFactor,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.writeSecsTotal, c.writeSecsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.AvgSecPerWrite*perftypes.TicksToSecondScaleFactor, data[AvgSecPerWrite].FirstValue*perftypes.TicksToSecondScaleFactor,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.requestSecs, c.requestSecs,
prometheus.CounterValue, prometheus.CounterValue,
instance.AvgSecPerDataRequest*perftypes.TicksToSecondScaleFactor, data[AvgSecPerDataRequest].FirstValue*perftypes.TicksToSecondScaleFactor,
serverValue, shareValue, serverValue, shareValue,
) )
@ -277,112 +269,112 @@ func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.L
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.writeRequestQueueSecsTotal, c.writeRequestQueueSecsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.AvgWriteQueueLength*perftypes.TicksToSecondScaleFactor, data[AvgWriteQueueLength].FirstValue*perftypes.TicksToSecondScaleFactor,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.creditStallsTotal, c.creditStallsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.CreditStallsPerSec, data[CreditStallsPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.currentDataQueued, c.currentDataQueued,
prometheus.GaugeValue, prometheus.GaugeValue,
instance.CurrentDataQueueLength, data[CurrentDataQueueLength].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.dataBytesTotal, c.dataBytesTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.DataBytesPerSec, data[DataBytesPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.dataRequestsTotal, c.dataRequestsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.DataRequestsPerSec, data[DataRequestsPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.metadataRequestsTotal, c.metadataRequestsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.MetadataRequestsPerSec, data[MetadataRequestsPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.readBytesTransmittedViaSMBDirectTotal, c.readBytesTransmittedViaSMBDirectTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.ReadBytesTransmittedViaSMBDirectPerSec, data[ReadBytesTransmittedViaSMBDirectPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.readBytesTotal, c.readBytesTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.ReadBytesPerSec, data[ReadBytesPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.readRequestsTransmittedViaSMBDirectTotal, c.readRequestsTransmittedViaSMBDirectTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.ReadRequestsTransmittedViaSMBDirectPerSec, data[ReadRequestsTransmittedViaSMBDirectPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.readsTotal, c.readsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.ReadRequestsPerSec, data[ReadRequestsPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.turboIOReadsTotal, c.turboIOReadsTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.TurboIOReadsPerSec, data[TurboIOReadsPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.TurboIOWritesTotal, c.TurboIOWritesTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.TurboIOWritesPerSec, data[TurboIOWritesPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.writeBytesTransmittedViaSMBDirectTotal, c.writeBytesTransmittedViaSMBDirectTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.WriteBytesTransmittedViaSMBDirectPerSec, data[WriteBytesTransmittedViaSMBDirectPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.writeBytesTotal, c.writeBytesTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.WriteBytesPerSec, data[WriteBytesPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.writeRequestsTransmittedViaSMBDirectTotal, c.writeRequestsTransmittedViaSMBDirectTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.WriteRequestsTransmittedViaSMBDirectPerSec, data[WriteRequestsTransmittedViaSMBDirectPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.writesTotal, c.writesTotal,
prometheus.CounterValue, prometheus.CounterValue,
instance.WriteRequestsPerSec, data[WriteRequestsPerSec].FirstValue,
serverValue, shareValue, serverValue, shareValue,
) )
} }