Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

an optional config file and a new HTTP query string to override walk param #601

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 63 additions & 14 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package config
import (
"fmt"
"io/ioutil"
"os"
"regexp"
"time"

Expand All @@ -36,6 +37,23 @@ func LoadFile(filename string) (*Config, error) {
return cfg, nil
}

func LoadWalkParamFile(filename string) (WalkConfig, error) {
content, err := ioutil.ReadFile(filename)
if err != nil {
if os.IsNotExist(err) {
return WalkConfig{}, nil
}
return nil, err
}
cfg := WalkConfig{}
err = yaml.UnmarshalStrict(content, &cfg)
if err != nil {
return nil, err
}

return cfg, nil
}

var (
DefaultAuth = Auth{
Community: "public",
Expand All @@ -61,6 +79,8 @@ var (
// Config for the snmp_exporter.
type Config map[string]*Module

type WalkConfig map[string]*WalkParams

type WalkParams struct {
Version int `yaml:"version,omitempty"`
MaxRepetitions uint8 `yaml:"max_repetitions,omitempty"`
Expand All @@ -77,6 +97,47 @@ type Module struct {
WalkParams WalkParams `yaml:",inline"`
}

func (c *WalkParams) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultWalkParams
type plain WalkParams
if err := unmarshal((*plain)(c)); err != nil {
return err
}

wp := c

if wp.Version < 1 || wp.Version > 3 {
return fmt.Errorf("SNMP version must be 1, 2 or 3. Got: %d", wp.Version)
}
if wp.Version == 3 {
switch wp.Auth.SecurityLevel {
case "authPriv":
if wp.Auth.PrivPassword == "" {
return fmt.Errorf("priv password is missing, required for SNMPv3 with priv")
}
if wp.Auth.PrivProtocol != "DES" && wp.Auth.PrivProtocol != "AES" {
return fmt.Errorf("priv protocol must be DES or AES")
}
fallthrough
case "authNoPriv":
if wp.Auth.Password == "" {
return fmt.Errorf("auth password is missing, required for SNMPv3 with auth")
}
if wp.Auth.AuthProtocol != "MD5" && wp.Auth.AuthProtocol != "SHA" {
return fmt.Errorf("auth protocol must be SHA or MD5")
}
fallthrough
case "noAuthNoPriv":
if wp.Auth.Username == "" {
return fmt.Errorf("auth username is missing, required for SNMPv3")
}
default:
return fmt.Errorf("security level must be one of authPriv, authNoPriv or noAuthNoPriv")
}
}
return nil
}

func (c *Module) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultModule
type plain Module
Expand All @@ -95,15 +156,15 @@ func (c *Module) UnmarshalYAML(unmarshal func(interface{}) error) error {
if wp.Auth.PrivPassword == "" {
return fmt.Errorf("priv password is missing, required for SNMPv3 with priv")
}
if wp.Auth.PrivProtocol != "DES" && wp.Auth.PrivProtocol != "AES" && wp.Auth.PrivProtocol != "AES192" && wp.Auth.PrivProtocol != "AES256" {
if wp.Auth.PrivProtocol != "DES" && wp.Auth.PrivProtocol != "AES" {
return fmt.Errorf("priv protocol must be DES or AES")
}
fallthrough
case "authNoPriv":
if wp.Auth.Password == "" {
return fmt.Errorf("auth password is missing, required for SNMPv3 with auth")
}
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" {
if wp.Auth.AuthProtocol != "MD5" && wp.Auth.AuthProtocol != "SHA" {
return fmt.Errorf("auth protocol must be SHA or MD5")
}
fallthrough
Expand Down Expand Up @@ -153,14 +214,6 @@ func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
switch c.Auth.AuthProtocol {
case "SHA":
usm.AuthenticationProtocol = gosnmp.SHA
case "SHA224":
usm.AuthenticationProtocol = gosnmp.SHA224
case "SHA256":
usm.AuthenticationProtocol = gosnmp.SHA256
case "SHA384":
usm.AuthenticationProtocol = gosnmp.SHA384
case "SHA512":
usm.AuthenticationProtocol = gosnmp.SHA512
case "MD5":
usm.AuthenticationProtocol = gosnmp.MD5
}
Expand All @@ -172,10 +225,6 @@ func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
usm.PrivacyProtocol = gosnmp.DES
case "AES":
usm.PrivacyProtocol = gosnmp.AES
case "AES192":
usm.PrivacyProtocol = gosnmp.AES192
case "AES256":
usm.PrivacyProtocol = gosnmp.AES256
}
}
g.SecurityParameters = usm
Expand Down
49 changes: 39 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,17 @@ import (
"github.com/prometheus/common/promlog"
"github.com/prometheus/common/promlog/flag"
"github.com/prometheus/common/version"
"github.com/prometheus/exporter-toolkit/web"
webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag"
"gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/yaml.v2"

"github.com/prometheus/snmp_exporter/config"
)

var (
configFile = kingpin.Flag("config.file", "Path to configuration file.").Default("snmp.yml").String()
webConfig = webflag.AddFlags(kingpin.CommandLine)
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9116").String()
dryRun = kingpin.Flag("dry-run", "Only verify configuration is valid and exit.").Default("false").Bool()
walkParamConfigFile = kingpin.Flag("config.walkParam", "path to optional walk param configuration file").Default("wp.yml").String()
configFile = kingpin.Flag("config.file", "Path to configuration file.").Default("snmp.yml").String()
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9116").String()
dryRun = kingpin.Flag("dry-run", "Only verify configuration is valid and exit.").Default("false").Bool()

// Metrics about the SNMP exporter itself.
snmpDuration = prometheus.NewSummaryVec(
Expand Down Expand Up @@ -98,7 +96,28 @@ func handler(w http.ResponseWriter, r *http.Request, logger log.Logger) {
return
}

logger = log.With(logger, "module", moduleName, "target", target)
var wpName = query.Get("walkParam")
if len(query["walkParaM"]) > 1 {
http.Error(w, "'walkParam' parameter must only be specified once", 400)
snmpRequestErrors.Inc()
return
}
if wpName == "" {
//nothing to do
} else {
if wp, exist := sc.wp[wpName]; exist == false {
http.Error(w, fmt.Sprintf("unknown 'walkParam' parameter '%v'", wpName), 400)
snmpRequestErrors.Inc()
return
} else {
module.WalkParams = *wp
}
}
if wpName == "" {
logger = log.With(logger, "module", moduleName, "target", target)
} else {
logger = log.With(logger, "module", moduleName, "walkParam", wpName, "target", target)
}
level.Debug(logger).Log("msg", "Starting scrape")

start := time.Now()
Expand Down Expand Up @@ -128,16 +147,22 @@ func updateConfiguration(w http.ResponseWriter, r *http.Request) {

type SafeConfig struct {
sync.RWMutex
C *config.Config
C *config.Config
wp config.WalkConfig
}

func (sc *SafeConfig) ReloadConfig(configFile string) (err error) {
conf, err := config.LoadFile(configFile)
if err != nil {
return err
}
wp, err := config.LoadWalkParamFile(*walkParamConfigFile)
if err != nil {
return err
}
sc.Lock()
sc.C = conf
sc.wp = wp
sc.Unlock()
return nil
}
Expand All @@ -159,6 +184,11 @@ func main() {
level.Error(logger).Log("msg", "Error parsing config file", "err", err)
os.Exit(1)
}
sc.wp, err = config.LoadWalkParamFile(*walkParamConfigFile)
if err != nil {
level.Error(logger).Log("msg", "Error parsing config file", "err", err)
os.Exit(1)
}

// Exit if in dry-run mode.
if *dryRun {
Expand Down Expand Up @@ -244,8 +274,7 @@ func main() {
})

level.Info(logger).Log("msg", "Listening on address", "address", *listenAddress)
srv := &http.Server{Addr: *listenAddress}
if err := web.ListenAndServe(srv, *webConfig, logger); err != nil {
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
level.Error(logger).Log("msg", "Error starting HTTP server", "err", err)
os.Exit(1)
}
Expand Down