Skip to content

Commit 4a7a6f0

Browse files
committed
Split config of auth and modules
Allow configuration of auth/version parameters separately from the walk and metrics in the generator and exporter configuration. * Simplify startup with `ReloadConfig()` * Make sure to init metrics on config reload. Fixes: #619 Signed-off-by: SuperQ <[email protected]>
1 parent 87bf22c commit 4a7a6f0

File tree

11 files changed

+57502
-57367
lines changed

11 files changed

+57502
-57367
lines changed

collector/collector.go

+14-13
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,19 @@ type ScrapeResults struct {
115115
retries uint64
116116
}
117117

118-
func ScrapeTarget(ctx context.Context, target string, config *config.Module, logger log.Logger) (ScrapeResults, error) {
118+
func ScrapeTarget(ctx context.Context, target string, auth *config.Auth, module *config.Module, logger log.Logger) (ScrapeResults, error) {
119119
results := ScrapeResults{}
120120
// Set the options.
121121
snmp := gosnmp.GoSNMP{}
122122
snmp.Context = ctx
123-
snmp.MaxRepetitions = config.WalkParams.MaxRepetitions
124-
snmp.Retries = *config.WalkParams.Retries
125-
snmp.Timeout = config.WalkParams.Timeout
126-
snmp.UseUnconnectedUDPSocket = config.WalkParams.UseUnconnectedUDPSocket
123+
snmp.MaxRepetitions = module.WalkParams.MaxRepetitions
124+
snmp.Retries = *module.WalkParams.Retries
125+
snmp.Timeout = module.WalkParams.Timeout
126+
snmp.UseUnconnectedUDPSocket = module.WalkParams.UseUnconnectedUDPSocket
127127
snmp.LocalAddr = *srcAddress
128128

129129
// Allow a set of OIDs that aren't in a strictly increasing order
130-
if config.WalkParams.AllowNonIncreasingOIDs {
130+
if module.WalkParams.AllowNonIncreasingOIDs {
131131
snmp.AppOpts = make(map[string]interface{})
132132
snmp.AppOpts["c"] = true
133133
}
@@ -158,7 +158,7 @@ func ScrapeTarget(ctx context.Context, target string, config *config.Module, log
158158
}
159159

160160
// Configure auth.
161-
config.WalkParams.ConfigureSNMP(&snmp)
161+
auth.ConfigureSNMP(&snmp)
162162

163163
// Do the actual walk.
164164
err := snmp.Connect()
@@ -170,8 +170,8 @@ func ScrapeTarget(ctx context.Context, target string, config *config.Module, log
170170
}
171171
defer snmp.Conn.Close()
172172

173-
getOids := config.Get
174-
maxOids := int(config.WalkParams.MaxRepetitions)
173+
getOids := module.Get
174+
maxOids := int(module.WalkParams.MaxRepetitions)
175175
// Max Repetition can be 0, maxOids cannot. SNMPv1 can only report one OID error per call.
176176
if maxOids == 0 || snmp.Version == gosnmp.Version1 {
177177
maxOids = 1
@@ -213,7 +213,7 @@ func ScrapeTarget(ctx context.Context, target string, config *config.Module, log
213213
getOids = getOids[oids:]
214214
}
215215

216-
for _, subtree := range config.Walk {
216+
for _, subtree := range module.Walk {
217217
var pdus []gosnmp.SnmpPDU
218218
level.Debug(logger).Log("msg", "Walking subtree", "oid", subtree)
219219
walkStart := time.Now()
@@ -261,12 +261,13 @@ func buildMetricTree(metrics []*config.Metric) *MetricNode {
261261
type collector struct {
262262
ctx context.Context
263263
target string
264+
auth *config.Auth
264265
module *config.Module
265266
logger log.Logger
266267
}
267268

268-
func New(ctx context.Context, target string, module *config.Module, logger log.Logger) *collector {
269-
return &collector{ctx: ctx, target: target, module: module, logger: logger}
269+
func New(ctx context.Context, target string, auth *config.Auth, module *config.Module, logger log.Logger) *collector {
270+
return &collector{ctx: ctx, target: target, auth: auth, module: module, logger: logger}
270271
}
271272

272273
// Describe implements Prometheus.Collector.
@@ -277,7 +278,7 @@ func (c collector) Describe(ch chan<- *prometheus.Desc) {
277278
// Collect implements Prometheus.Collector.
278279
func (c collector) Collect(ch chan<- prometheus.Metric) {
279280
start := time.Now()
280-
results, err := ScrapeTarget(c.ctx, c.target, c.module, c.logger)
281+
results, err := ScrapeTarget(c.ctx, c.target, c.auth, c.module, c.logger)
281282
if err != nil {
282283
level.Info(c.logger).Log("msg", "Error scraping target", "err", err)
283284
ch <- prometheus.NewInvalidMetric(prometheus.NewDesc("snmp_error", "Error scraping target", nil, nil), err)

config/config.go

+54-46
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ var (
4444
SecurityLevel: "noAuthNoPriv",
4545
AuthProtocol: "MD5",
4646
PrivProtocol: "DES",
47+
Version: 2,
4748
}
4849
DefaultWalkParams = WalkParams{
49-
Version: 2,
5050
MaxRepetitions: 25,
5151
Retries: &defaultRetries,
5252
Timeout: time.Second * 5,
53-
Auth: DefaultAuth,
5453
UseUnconnectedUDPSocket: false,
5554
AllowNonIncreasingOIDs: false,
5655
}
@@ -63,14 +62,15 @@ var (
6362
)
6463

6564
// Config for the snmp_exporter.
66-
type Config map[string]*Module
65+
type Config struct {
66+
Auths map[string]*Auth `yaml:"auths",omitempty"`
67+
Modules map[string]*Module `yaml:"modules",omitempty"`
68+
}
6769

6870
type WalkParams struct {
69-
Version int `yaml:"version,omitempty"`
7071
MaxRepetitions uint32 `yaml:"max_repetitions,omitempty"`
7172
Retries *int `yaml:"retries,omitempty"`
7273
Timeout time.Duration `yaml:"timeout,omitempty"`
73-
Auth Auth `yaml:"auth,omitempty"`
7474
UseUnconnectedUDPSocket bool `yaml:"use_unconnected_udp_socket,omitempty"`
7575
AllowNonIncreasingOIDs bool `yaml:"allow_nonincreasing_oids,omitempty"`
7676
}
@@ -89,43 +89,11 @@ func (c *Module) UnmarshalYAML(unmarshal func(interface{}) error) error {
8989
if err := unmarshal((*plain)(c)); err != nil {
9090
return err
9191
}
92-
93-
wp := c.WalkParams
94-
95-
if wp.Version < 1 || wp.Version > 3 {
96-
return fmt.Errorf("SNMP version must be 1, 2 or 3. Got: %d", wp.Version)
97-
}
98-
if wp.Version == 3 {
99-
switch wp.Auth.SecurityLevel {
100-
case "authPriv":
101-
if wp.Auth.PrivPassword == "" {
102-
return fmt.Errorf("priv password is missing, required for SNMPv3 with priv")
103-
}
104-
if wp.Auth.PrivProtocol != "DES" && wp.Auth.PrivProtocol != "AES" && wp.Auth.PrivProtocol != "AES192" && wp.Auth.PrivProtocol != "AES192C" && wp.Auth.PrivProtocol != "AES256" && wp.Auth.PrivProtocol != "AES256C" {
105-
return fmt.Errorf("priv protocol must be DES or AES")
106-
}
107-
fallthrough
108-
case "authNoPriv":
109-
if wp.Auth.Password == "" {
110-
return fmt.Errorf("auth password is missing, required for SNMPv3 with auth")
111-
}
112-
if wp.Auth.AuthProtocol != "MD5" && wp.Auth.AuthProtocol != "SHA" && wp.Auth.AuthProtocol != "SHA224" && wp.Auth.AuthProtocol != "SHA256" && wp.Auth.AuthProtocol != "SHA384" && wp.Auth.AuthProtocol != "SHA512" {
113-
return fmt.Errorf("auth protocol must be SHA or MD5")
114-
}
115-
fallthrough
116-
case "noAuthNoPriv":
117-
if wp.Auth.Username == "" {
118-
return fmt.Errorf("auth username is missing, required for SNMPv3")
119-
}
120-
default:
121-
return fmt.Errorf("security level must be one of authPriv, authNoPriv or noAuthNoPriv")
122-
}
123-
}
12492
return nil
12593
}
12694

12795
// ConfigureSNMP sets the various version and auth settings.
128-
func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
96+
func (c Auth) ConfigureSNMP(g *gosnmp.GoSNMP) {
12997
switch c.Version {
13098
case 1:
13199
g.Version = gosnmp.Version1
@@ -134,16 +102,16 @@ func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
134102
case 3:
135103
g.Version = gosnmp.Version3
136104
}
137-
g.Community = string(c.Auth.Community)
138-
g.ContextName = c.Auth.ContextName
105+
g.Community = string(c.Community)
106+
g.ContextName = c.ContextName
139107

140108
// v3 security settings.
141109
g.SecurityModel = gosnmp.UserSecurityModel
142110
usm := &gosnmp.UsmSecurityParameters{
143-
UserName: c.Auth.Username,
111+
UserName: c.Username,
144112
}
145113
auth, priv := false, false
146-
switch c.Auth.SecurityLevel {
114+
switch c.SecurityLevel {
147115
case "noAuthNoPriv":
148116
g.MsgFlags = gosnmp.NoAuthNoPriv
149117
case "authNoPriv":
@@ -155,8 +123,8 @@ func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
155123
priv = true
156124
}
157125
if auth {
158-
usm.AuthenticationPassphrase = string(c.Auth.Password)
159-
switch c.Auth.AuthProtocol {
126+
usm.AuthenticationPassphrase = string(c.Password)
127+
switch c.AuthProtocol {
160128
case "SHA":
161129
usm.AuthenticationProtocol = gosnmp.SHA
162130
case "SHA224":
@@ -172,8 +140,8 @@ func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
172140
}
173141
}
174142
if priv {
175-
usm.PrivacyPassphrase = string(c.Auth.PrivPassword)
176-
switch c.Auth.PrivProtocol {
143+
usm.PrivacyPassphrase = string(c.PrivPassword)
144+
switch c.PrivProtocol {
177145
case "DES":
178146
usm.PrivacyProtocol = gosnmp.DES
179147
case "AES":
@@ -245,6 +213,46 @@ type Auth struct {
245213
PrivProtocol string `yaml:"priv_protocol,omitempty"`
246214
PrivPassword Secret `yaml:"priv_password,omitempty"`
247215
ContextName string `yaml:"context_name,omitempty"`
216+
Version int `yaml:"version,omitempty"`
217+
}
218+
219+
func (c *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error {
220+
*c = DefaultAuth
221+
type plain Auth
222+
if err := unmarshal((*plain)(c)); err != nil {
223+
return err
224+
}
225+
226+
if c.Version < 1 || c.Version > 3 {
227+
return fmt.Errorf("SNMP version must be 1, 2 or 3. Got: %d", c.Version)
228+
}
229+
if c.Version == 3 {
230+
switch c.SecurityLevel {
231+
case "authPriv":
232+
if c.PrivPassword == "" {
233+
return fmt.Errorf("priv password is missing, required for SNMPv3 with priv")
234+
}
235+
if c.PrivProtocol != "DES" && c.PrivProtocol != "AES" && c.PrivProtocol != "AES192" && c.PrivProtocol != "AES192C" && c.PrivProtocol != "AES256" && c.PrivProtocol != "AES256C" {
236+
return fmt.Errorf("priv protocol must be DES or AES")
237+
}
238+
fallthrough
239+
case "authNoPriv":
240+
if c.Password == "" {
241+
return fmt.Errorf("auth password is missing, required for SNMPv3 with auth")
242+
}
243+
if c.AuthProtocol != "MD5" && c.AuthProtocol != "SHA" && c.AuthProtocol != "SHA224" && c.AuthProtocol != "SHA256" && c.AuthProtocol != "SHA384" && c.AuthProtocol != "SHA512" {
244+
return fmt.Errorf("auth protocol must be SHA or MD5")
245+
}
246+
fallthrough
247+
case "noAuthNoPriv":
248+
if c.Username == "" {
249+
return fmt.Errorf("auth username is missing, required for SNMPv3")
250+
}
251+
default:
252+
return fmt.Errorf("security level must be one of authPriv, authNoPriv or noAuthNoPriv")
253+
}
254+
}
255+
return nil
248256
}
249257

250258
type RegexpExtract struct {

generator/FORMAT.md

+58-56
Original file line numberDiff line numberDiff line change
@@ -4,62 +4,64 @@ This is generated by the generator, so only those doing development should
44
have to care about how this works.
55

66
```
7-
module_name:
8-
auth:
7+
auths:
8+
public:
99
# There's various auth/version options here too. See the main README.
1010
community: public
11-
walk:
12-
# List of OID subtrees to walk.
13-
- 1.3.6.1.2.1.2
14-
- 1.3.6.1.2.1.31.1.1
15-
get:
16-
# List of OIDs to get directly.
17-
- 1.3.6.1.2.1.1.3
18-
metrics: # List of metrics to extract.
19-
# A simple metric with no labels.
20-
- name: sysUpTime
21-
oid: 1.3.6.1.2.1.1.3
22-
type: gauge
23-
# See README.md type override for a list of valid types
24-
# Non-numeric types are represented as a gauge with value 1, and the rendered value
25-
# as a label value on that gauge.
26-
27-
# A metric that's part of a table, and thus has labels.
28-
- name: ifMtu
29-
oid: 1.3.6.1.2.1.2.2.1.4
30-
type: gauge
31-
# A list of the table indexes and their types. All indexes become labels.
32-
indexes:
33-
- labelname: ifIndex
34-
type: gauge
35-
- labelname: someString
36-
type: OctetString
37-
fixed_size: 8 # Only possible for OctetString/DisplayString types.
38-
# If only one length is possible this is it. Otherwise
39-
# this will be 0 or missing.
40-
- labelname: someOtherString
41-
type: OctetString
42-
implied: true # Only possible for OctetString/DisplayString types.
43-
# Must be the last index. See RFC2578 section 7.7.
44-
- name: ifSpeed
45-
oid: 1.3.6.1.2.1.2.2.1.5
46-
type: gauge
47-
indexes:
48-
- labelname: ifDescr
49-
type: gauge
50-
# Lookups take original indexes, look them up in another part of the
51-
# oid tree and overwrite the given output label.
52-
lookups:
53-
- labels: [ifDescr] # Input label name(s). Empty means delete the output label.
54-
oid: 1.3.6.1.2.1.2.2.1.2 # OID to look under.
55-
labelname: ifDescr # Output label name.
56-
type: OctetString # Type of output object.
57-
# Creates new metrics based on the regex and the metric value.
58-
regex_extracts:
59-
Temp: # A new metric will be created appending this to the metricName to become metricNameTemp.
60-
- regex: '(.*)' # Regex to extract a value from the returned SNMP walks's value.
61-
value: '$1' # Parsed as float64, defaults to $1.
62-
enum_values: # Enum for this metric. Only used with the enum types.
63-
0: true
64-
1: false
11+
modules:
12+
module_name:
13+
walk:
14+
# List of OID subtrees to walk.
15+
- 1.3.6.1.2.1.2
16+
- 1.3.6.1.2.1.31.1.1
17+
get:
18+
# List of OIDs to get directly.
19+
- 1.3.6.1.2.1.1.3
20+
metrics: # List of metrics to extract.
21+
# A simple metric with no labels.
22+
- name: sysUpTime
23+
oid: 1.3.6.1.2.1.1.3
24+
type: gauge
25+
# See README.md type override for a list of valid types
26+
# Non-numeric types are represented as a gauge with value 1, and the rendered value
27+
# as a label value on that gauge.
28+
29+
# A metric that's part of a table, and thus has labels.
30+
- name: ifMtu
31+
oid: 1.3.6.1.2.1.2.2.1.4
32+
type: gauge
33+
# A list of the table indexes and their types. All indexes become labels.
34+
indexes:
35+
- labelname: ifIndex
36+
type: gauge
37+
- labelname: someString
38+
type: OctetString
39+
fixed_size: 8 # Only possible for OctetString/DisplayString types.
40+
# If only one length is possible this is it. Otherwise
41+
# this will be 0 or missing.
42+
- labelname: someOtherString
43+
type: OctetString
44+
implied: true # Only possible for OctetString/DisplayString types.
45+
# Must be the last index. See RFC2578 section 7.7.
46+
- name: ifSpeed
47+
oid: 1.3.6.1.2.1.2.2.1.5
48+
type: gauge
49+
indexes:
50+
- labelname: ifDescr
51+
type: gauge
52+
# Lookups take original indexes, look them up in another part of the
53+
# oid tree and overwrite the given output label.
54+
lookups:
55+
- labels: [ifDescr] # Input label name(s). Empty means delete the output label.
56+
oid: 1.3.6.1.2.1.2.2.1.2 # OID to look under.
57+
labelname: ifDescr # Output label name.
58+
type: OctetString # Type of output object.
59+
# Creates new metrics based on the regex and the metric value.
60+
regex_extracts:
61+
Temp: # A new metric will be created appending this to the metricName to become metricNameTemp.
62+
- regex: '(.*)' # Regex to extract a value from the returned SNMP walks's value.
63+
value: '$1' # Parsed as float64, defaults to $1.
64+
enum_values: # Enum for this metric. Only used with the enum types.
65+
0: true
66+
1: false
6567
```

0 commit comments

Comments
 (0)