mirror of
https://github.com/prometheus-community/smartctl_exporter.git
synced 2024-12-21 02:21:55 +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.device=SMARTCTL.DEVICE ...
|
||||
The device to monitor (repeatable)
|
||||
--web.listen-address=":9633"
|
||||
Address to listen on for web interface and telemetry
|
||||
--smartctl.device-exclude=""
|
||||
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"
|
||||
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.
|
||||
--log.level=info Only log messages with the given severity or above. One of: [debug, info, warn,
|
||||
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",
|
||||
"The device to monitor (repeatable)",
|
||||
).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",
|
||||
"The device to monitor (repeatable)",
|
||||
).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() {
|
||||
metricsPath := kingpin.Flag(
|
||||
"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", "Build context", "build_context", version.BuildContext())
|
||||
|
||||
// Scan the host devices
|
||||
json := readSMARTctlDevices(logger)
|
||||
scanDevices := json.Get("devices").Array()
|
||||
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
|
||||
var devices []string
|
||||
if len(*smartctlDevices) > 0 {
|
||||
devices = *smartctlDevices
|
||||
} else {
|
||||
level.Info(logger).Log("msg", "No devices specified, trying to load them automatically")
|
||||
devices = scanDeviceNames
|
||||
devices = scanDevices(logger)
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
|
|
Loading…
Reference in a new issue