diff --git a/README.md b/README.md index a592bbe7..87dd22b5 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Name | Description | Enabled by default [remote_fx](docs/collector.remote_fx.md) | RemoteFX protocol (RDP) metrics | [scheduled_task](docs/collector.scheduled_task.md) | Scheduled Tasks metrics | [service](docs/collector.service.md) | Service state metrics | ✓ +[smb](docs/collector.smb.md) | SMB Server | [smtp](docs/collector.smtp.md) | IIS SMTP Server | [system](docs/collector.system.md) | System calls | ✓ [tcp](docs/collector.tcp.md) | TCP connections | diff --git a/docs/collector.smb.md b/docs/collector.smb.md new file mode 100644 index 00000000..dee54711 --- /dev/null +++ b/docs/collector.smb.md @@ -0,0 +1,35 @@ +# smb collector + +The smb collector collects metrics from MS Smb hosts through perflib +======= + + +||| +-|- +Metric name prefix | `smb` +Classes | [Win32_PerfRawData_SMB](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb/)
+Enabled by default? | No + +## Flags + +### `--collectors.smb.list` +Lists the Perflib Objects that are queried for data along with the perlfib object id + +### `--collectors.smb.enabled` +Comma-separated list of collectors to use, for example: `--collectors.smb.enabled=ServerShares`. Matching is case-sensitive. Depending on the smb installation not all performance counters are available. Use `--collectors.smb.list` to obtain a list of supported collectors. + +## Metrics +Name | Description +--------------|--------------- +`windows_smb_server_shares_current_open_file_count` | Current total count open files on the SMB Server +`windows_smb_server_shares_tree_connect_count` | Count of user connections to the SMB Server + +### Example metric +_This collector does not yet have explained examples, we would appreciate your help adding them!_ + +## Useful queries +_This collector does not yet have any useful queries added, we would appreciate your help adding them!_ + +## Alerting examples +_This collector does not yet have alerting examples, we would appreciate your help adding them!_ + diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 87184c11..297020b5 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -49,6 +49,7 @@ import ( "github.com/prometheus-community/windows_exporter/pkg/collector/remote_fx" "github.com/prometheus-community/windows_exporter/pkg/collector/scheduled_task" "github.com/prometheus-community/windows_exporter/pkg/collector/service" + "github.com/prometheus-community/windows_exporter/pkg/collector/smb" "github.com/prometheus-community/windows_exporter/pkg/collector/smtp" "github.com/prometheus-community/windows_exporter/pkg/collector/system" "github.com/prometheus-community/windows_exporter/pkg/collector/tcp" @@ -126,6 +127,7 @@ func NewWithConfig(logger log.Logger, config Config) Collectors { collectors[remote_fx.Name] = remote_fx.New(logger, &config.RemoteFx) collectors[scheduled_task.Name] = scheduled_task.New(logger, &config.ScheduledTask) collectors[service.Name] = service.New(logger, &config.Service) + collectors[smb.Name] = smb.New(logger, &config.Smb) collectors[smtp.Name] = smtp.New(logger, &config.Smtp) collectors[system.Name] = system.New(logger, &config.System) collectors[teradici_pcoip.Name] = teradici_pcoip.New(logger, &config.TeradiciPcoip) diff --git a/pkg/collector/config.go b/pkg/collector/config.go index 64038af6..3dde3226 100644 --- a/pkg/collector/config.go +++ b/pkg/collector/config.go @@ -42,6 +42,7 @@ import ( "github.com/prometheus-community/windows_exporter/pkg/collector/remote_fx" "github.com/prometheus-community/windows_exporter/pkg/collector/scheduled_task" "github.com/prometheus-community/windows_exporter/pkg/collector/service" + "github.com/prometheus-community/windows_exporter/pkg/collector/smb" "github.com/prometheus-community/windows_exporter/pkg/collector/smtp" "github.com/prometheus-community/windows_exporter/pkg/collector/system" "github.com/prometheus-community/windows_exporter/pkg/collector/tcp" @@ -97,6 +98,7 @@ type Config struct { RemoteFx remote_fx.Config `yaml:"remote_fx"` ScheduledTask scheduled_task.Config `yaml:"scheduled_task"` Service service.Config `yaml:"service"` + Smb smb.Config `yaml:"smb"` Smtp smtp.Config `yaml:"smtp"` System system.Config `yaml:"system"` TeradiciPcoip teradici_pcoip.Config `yaml:"teradici_pcoip"` @@ -153,6 +155,7 @@ var ConfigDefaults = Config{ RemoteFx: remote_fx.ConfigDefaults, ScheduledTask: scheduled_task.ConfigDefaults, Service: service.ConfigDefaults, + Smb: smb.ConfigDefaults, Smtp: smtp.ConfigDefaults, System: system.ConfigDefaults, TeradiciPcoip: teradici_pcoip.ConfigDefaults, diff --git a/pkg/collector/map.go b/pkg/collector/map.go index fc5c7e77..7a6d4daf 100644 --- a/pkg/collector/map.go +++ b/pkg/collector/map.go @@ -43,6 +43,7 @@ import ( "github.com/prometheus-community/windows_exporter/pkg/collector/remote_fx" "github.com/prometheus-community/windows_exporter/pkg/collector/scheduled_task" "github.com/prometheus-community/windows_exporter/pkg/collector/service" + "github.com/prometheus-community/windows_exporter/pkg/collector/smb" "github.com/prometheus-community/windows_exporter/pkg/collector/smtp" "github.com/prometheus-community/windows_exporter/pkg/collector/system" "github.com/prometheus-community/windows_exporter/pkg/collector/tcp" @@ -101,6 +102,7 @@ var Map = map[string]types.CollectorBuilderWithFlags{ remote_fx.Name: remote_fx.NewWithFlags, scheduled_task.Name: scheduled_task.NewWithFlags, service.Name: service.NewWithFlags, + smb.Name: smb.NewWithFlags, smtp.Name: smtp.NewWithFlags, system.Name: system.NewWithFlags, teradici_pcoip.Name: teradici_pcoip.NewWithFlags, diff --git a/pkg/collector/smb/smb.go b/pkg/collector/smb/smb.go new file mode 100644 index 00000000..0b2cbb13 --- /dev/null +++ b/pkg/collector/smb/smb.go @@ -0,0 +1,192 @@ +//go:build windows + +package smb + +import ( + "fmt" + "os" + "slices" + "strings" + + "github.com/alecthomas/kingpin/v2" + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/prometheus-community/windows_exporter/pkg/perflib" + "github.com/prometheus-community/windows_exporter/pkg/types" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + Name = "smb" + FlagSmbListAllCollectors = "collectors.smb.list" + FlagSmbCollectorsEnabled = "collectors.smb.enabled" +) + +type Config struct { + CollectorsEnabled string `yaml:"collectors_enabled"` +} + +var ConfigDefaults = Config{ + CollectorsEnabled: "", +} + +type collector struct { + logger log.Logger + + smbListAllCollectors *bool + smbCollectorsEnabled *string + + TreeConnectCount *prometheus.Desc + CurrentOpenFileCount *prometheus.Desc + + enabledCollectors []string +} + +// All available collector functions +var smbAllCollectorNames = []string{ + "ServerShares", +} + +func New(logger log.Logger, config *Config) types.Collector { + if config == nil { + config = &ConfigDefaults + } + + smbListAllCollectors := false + c := &collector{ + smbCollectorsEnabled: &config.CollectorsEnabled, + smbListAllCollectors: &smbListAllCollectors, + } + c.SetLogger(logger) + return c +} + +func NewWithFlags(app *kingpin.Application) types.Collector { + return &collector{ + smbListAllCollectors: app.Flag( + FlagSmbListAllCollectors, + "List the collectors along with their perflib object name/ids", + ).Bool(), + + smbCollectorsEnabled: app.Flag( + FlagSmbCollectorsEnabled, + "Comma-separated list of collectors to use. Defaults to all, if not specified.", + ).Default(ConfigDefaults.CollectorsEnabled).String(), + } +} + +func (c *collector) GetName() string { + return Name +} + +func (c *collector) SetLogger(logger log.Logger) { + c.logger = log.With(logger, "collector", Name) +} + +func (c *collector) GetPerfCounter() ([]string, error) { + return []string{ + "SMB Server Shares", + }, nil +} + +func (c *collector) Build() error { + // desc creates a new prometheus description + desc := func(metricName string, description string, labels ...string) *prometheus.Desc { + return prometheus.NewDesc( + prometheus.BuildFQName(types.Namespace, "smb", metricName), + description, + labels, + nil, + ) + } + + c.CurrentOpenFileCount = desc("server_shares_current_open_file_count", "Current total count open files on the SMB Server") + c.TreeConnectCount = desc("server_shares_tree_connect_count", "Count of user connections to the SMB Server") + + c.enabledCollectors = make([]string, 0, len(smbAllCollectorNames)) + + collectorDesc := map[string]string{ + "ServerShares": "SMB Server Shares", + } + + if *c.smbListAllCollectors { + fmt.Printf("%-32s %-32s\n", "Collector Name", "Perflib Object") + for _, cname := range smbAllCollectorNames { + fmt.Printf("%-32s %-32s\n", cname, collectorDesc[cname]) + } + os.Exit(0) + } + + if *c.smbCollectorsEnabled == "" { + for _, collectorName := range smbAllCollectorNames { + c.enabledCollectors = append(c.enabledCollectors, collectorName) + } + } else { + for _, collectorName := range strings.Split(*c.smbCollectorsEnabled, ",") { + if slices.Contains(smbAllCollectorNames, collectorName) { + c.enabledCollectors = append(c.enabledCollectors, collectorName) + } else { + return fmt.Errorf("unknown smb collector: %s", collectorName) + } + } + } + + return nil +} + +// Collect collects smb metrics and sends them to prometheus +func (c *collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error { + collectorFuncs := map[string]func(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error{ + "ServerShares": c.collectServerShares, + } + + for _, collectorName := range c.enabledCollectors { + if err := collectorFuncs[collectorName](ctx, ch); err != nil { + _ = level.Error(c.logger).Log("msg", "Error in "+collectorName, "err", err) + return err + } + } + return nil +} + +// Perflib: SMB Server Shares +type perflibServerShares struct { + Name string + + CurrentOpenFileCount float64 `perflib:"Current Open File Count"` + TreeConnectCount float64 `perflib:"Tree Connect Count"` +} + +func (c *collector) collectServerShares(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibServerShares + if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Server Shares"], &data, c.logger); err != nil { + return err + } + for _, instance := range data { + labelName := c.toLabelName(instance.Name) + if !strings.HasSuffix(labelName, "_total") { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.CurrentOpenFileCount, + prometheus.CounterValue, + instance.CurrentOpenFileCount, + ) + + ch <- prometheus.MustNewConstMetric( + c.TreeConnectCount, + prometheus.CounterValue, + instance.TreeConnectCount, + ) + + } + 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 +} diff --git a/pkg/collector/smb/smb_test.go b/pkg/collector/smb/smb_test.go new file mode 100644 index 00000000..69c41eb8 --- /dev/null +++ b/pkg/collector/smb/smb_test.go @@ -0,0 +1,12 @@ +package smb_test + +import ( + "testing" + + "github.com/prometheus-community/windows_exporter/pkg/collector/smb" + "github.com/prometheus-community/windows_exporter/pkg/testutils" +) + +func BenchmarkCollector(b *testing.B) { + testutils.FuncBenchmarkCollector(b, smb.Name, smb.NewWithFlags) +}