mirror of
https://github.com/prometheus-community/smartctl_exporter.git
synced 2024-11-16 01:33:07 +01:00
Add device filtering
* Add device include/exclude filters for the automatic scanning. * Refactor scanning to not scan if devices are manually specified. * Don't try and filter manually specified devices. Signed-off-by: SuperQ <superq@gmail.com>
This commit is contained in:
parent
a58c632ea8
commit
fff5f67ae5
4 changed files with 125 additions and 28 deletions
12
README.md
12
README.md
|
@ -28,10 +28,18 @@ Flags:
|
||||||
--smartctl.interval=60s The interval between smarctl polls
|
--smartctl.interval=60s The interval between smarctl polls
|
||||||
--smartctl.device=SMARTCTL.DEVICE ...
|
--smartctl.device=SMARTCTL.DEVICE ...
|
||||||
The device to monitor (repeatable)
|
The device to monitor (repeatable)
|
||||||
--web.listen-address=":9633"
|
--smartctl.device-exclude=""
|
||||||
Address to listen on for web interface and telemetry
|
Regexp of devices to exclude from automatic scanning. (mutually exclusive to
|
||||||
|
device-include)
|
||||||
|
--smartctl.device-include=""
|
||||||
|
Regexp of devices to exclude from automatic scanning. (mutually exclusive to
|
||||||
|
device-exclude)
|
||||||
--web.telemetry-path="/metrics"
|
--web.telemetry-path="/metrics"
|
||||||
Path under which to expose metrics
|
Path under which to expose metrics
|
||||||
|
--web.systemd-socket Use systemd socket activation listeners instead of port listeners (Linux only).
|
||||||
|
--web.listen-address=:9633 ...
|
||||||
|
Addresses on which to expose metrics and web interface. Repeatable for multiple
|
||||||
|
addresses.
|
||||||
--web.config.file="" [EXPERIMENTAL] Path to configuration file that can enable TLS or authentication.
|
--web.config.file="" [EXPERIMENTAL] Path to configuration file that can enable TLS or authentication.
|
||||||
--log.level=info Only log messages with the given severity or above. One of: [debug, info, warn,
|
--log.level=info Only log messages with the given severity or above. One of: [debug, info, warn,
|
||||||
error]
|
error]
|
||||||
|
|
41
device_filter.go
Normal file
41
device_filter.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2022 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deviceFilter struct {
|
||||||
|
ignorePattern *regexp.Regexp
|
||||||
|
acceptPattern *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeviceFilter(ignoredPattern, acceptPattern string) (f deviceFilter) {
|
||||||
|
if ignoredPattern != "" {
|
||||||
|
f.ignorePattern = regexp.MustCompile(ignoredPattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
if acceptPattern != "" {
|
||||||
|
f.acceptPattern = regexp.MustCompile(acceptPattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignored returns whether the device should be ignored
|
||||||
|
func (f *deviceFilter) ignored(name string) bool {
|
||||||
|
return ((f.ignorePattern != nil && f.ignorePattern.MatchString(name)) ||
|
||||||
|
(f.acceptPattern != nil && !f.acceptPattern.MatchString(name)))
|
||||||
|
}
|
43
device_filter_test.go
Normal file
43
device_filter_test.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2022 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeviceFilter(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
ignore string
|
||||||
|
accept string
|
||||||
|
name string
|
||||||
|
expectedResult bool
|
||||||
|
}{
|
||||||
|
{"", "", "eth0", false},
|
||||||
|
{"", "^💩0$", "💩0", false},
|
||||||
|
{"", "^💩0$", "💩1", true},
|
||||||
|
{"", "^💩0$", "veth0", true},
|
||||||
|
{"^💩", "", "💩3", true},
|
||||||
|
{"^💩", "", "veth0", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
filter := newDeviceFilter(test.ignore, test.accept)
|
||||||
|
result := filter.ignored(test.name)
|
||||||
|
|
||||||
|
if result != test.expectedResult {
|
||||||
|
t.Errorf("ignorePattern=%v acceptPattern=%v ifname=%v expected=%v result=%v", test.ignore, test.accept, test.name, test.expectedResult, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
main.go
57
main.go
|
@ -69,11 +69,38 @@ var (
|
||||||
smartctlDevices = kingpin.Flag("smartctl.device",
|
smartctlDevices = kingpin.Flag("smartctl.device",
|
||||||
"The device to monitor (repeatable)",
|
"The device to monitor (repeatable)",
|
||||||
).Strings()
|
).Strings()
|
||||||
|
smartctlDeviceExclude = kingpin.Flag(
|
||||||
|
"smartctl.device-exclude",
|
||||||
|
"Regexp of devices to exclude from automatic scanning. (mutually exclusive to device-include)",
|
||||||
|
).Default("").String()
|
||||||
|
smartctlDeviceInclude = kingpin.Flag(
|
||||||
|
"smartctl.device-include",
|
||||||
|
"Regexp of devices to exclude from automatic scanning. (mutually exclusive to device-exclude)",
|
||||||
|
).Default("").String()
|
||||||
smartctlFakeData = kingpin.Flag("smartctl.fake-data",
|
smartctlFakeData = kingpin.Flag("smartctl.fake-data",
|
||||||
"The device to monitor (repeatable)",
|
"The device to monitor (repeatable)",
|
||||||
).Default("false").Hidden().Bool()
|
).Default("false").Hidden().Bool()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// scanDevices uses smartctl to gather the list of available devices.
|
||||||
|
func scanDevices(logger log.Logger) []string {
|
||||||
|
filter := newDeviceFilter(*smartctlDeviceExclude, *smartctlDeviceInclude)
|
||||||
|
|
||||||
|
json := readSMARTctlDevices(logger)
|
||||||
|
scanDevices := json.Get("devices").Array()
|
||||||
|
var scanDeviceResult []string
|
||||||
|
for _, d := range scanDevices {
|
||||||
|
deviceName := d.Get("name").String()
|
||||||
|
if filter.ignored(deviceName) {
|
||||||
|
level.Info(logger).Log("msg", "Ignoring device", "name", deviceName)
|
||||||
|
} else {
|
||||||
|
level.Info(logger).Log("msg", "Found device", "name", deviceName)
|
||||||
|
scanDeviceResult = append(scanDeviceResult, deviceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scanDeviceResult
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
metricsPath := kingpin.Flag(
|
metricsPath := kingpin.Flag(
|
||||||
"web.telemetry-path", "Path under which to expose metrics",
|
"web.telemetry-path", "Path under which to expose metrics",
|
||||||
|
@ -90,34 +117,12 @@ func main() {
|
||||||
level.Info(logger).Log("msg", "Starting smartctl_exporter", "version", version.Info())
|
level.Info(logger).Log("msg", "Starting smartctl_exporter", "version", version.Info())
|
||||||
level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext())
|
level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext())
|
||||||
|
|
||||||
// Scan the host devices
|
var devices []string
|
||||||
json := readSMARTctlDevices(logger)
|
if len(*smartctlDevices) > 0 {
|
||||||
scanDevices := json.Get("devices").Array()
|
devices = *smartctlDevices
|
||||||
scanDevicesSet := make(map[string]bool)
|
|
||||||
var scanDeviceNames []string
|
|
||||||
for _, d := range scanDevices {
|
|
||||||
deviceName := d.Get("name").String()
|
|
||||||
level.Debug(logger).Log("msg", "Found device", "name", deviceName)
|
|
||||||
scanDevicesSet[deviceName] = true
|
|
||||||
scanDeviceNames = append(scanDeviceNames, deviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the configuration and verify that it is available
|
|
||||||
devices := *smartctlDevices
|
|
||||||
var readDeviceNames []string
|
|
||||||
for _, device := range devices {
|
|
||||||
if _, ok := scanDevicesSet[device]; ok {
|
|
||||||
readDeviceNames = append(readDeviceNames, device)
|
|
||||||
} else {
|
|
||||||
level.Warn(logger).Log("msg", "Device unavailable", "name", device)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(readDeviceNames) > 0 {
|
|
||||||
devices = readDeviceNames
|
|
||||||
} else {
|
} else {
|
||||||
level.Info(logger).Log("msg", "No devices specified, trying to load them automatically")
|
level.Info(logger).Log("msg", "No devices specified, trying to load them automatically")
|
||||||
devices = scanDeviceNames
|
devices = scanDevices(logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(devices) == 0 {
|
if len(devices) == 0 {
|
||||||
|
|
Loading…
Reference in a new issue