Skip to content

Commit

Permalink
Enable passing input file, output file, and mibs dir on command line …
Browse files Browse the repository at this point in the history
…for the generate command
  • Loading branch information
dnck committed Oct 18, 2023
1 parent 2cd2b5c commit 3531baf
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 16 deletions.
14 changes: 12 additions & 2 deletions generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ make generator mibs
```
## Preparation

It is recommended to have a directory per device family which contains the mibs dir for the device family,
It is recommended to have a directory per device family which contains the mibs dir for the device family,
a logical link to the generator executable and the generator.yml configuration file. This is to avoid name space collisions
in the MIB definition. Keep only the required MIBS in the mibs directory for the devices.
Then merge all the resulting snmp.yml files into one main file that will be used by the snmp_exporter collector.
Expand All @@ -35,6 +35,16 @@ by the snmp_exporter executable to collect data from the snmp enabled devices.

Additional command are available for debugging, use the `help` command to see them.

After building, you can pass a directory of mibs, a path to the `generator.yml`
file and the intended path of your output file e.g. `snmp.yml` to the `generate`
command like so,
```bash
./generator generate \
-m /tmp
-g /tmp/generator.yml \
-o /tmp/snmp.yml \
```

### MIB Parsing options

The parsing of MIBs can be controlled using the `--snmp.mibopts` flag. The available values depend on the net-snmp version used to build the generator.
Expand Down Expand Up @@ -171,7 +181,7 @@ modules:
# If one of the target OIDs is used in a lookup, the filter will apply ALL tables using this lookup
# For a network switch, this could be used to collect a subset of interfaces such as uplinks
# For a router, this could be used to collect all real ports but not vlans and other virtual interfaces
# Specifying ifAlias or ifName if they are used in lookups with ifIndex will apply to the filter to
# Specifying ifAlias or ifName if they are used in lookups with ifIndex will apply to the filter to
# all the OIDs that depend on the lookup, such as ifSpeed, ifInHcOctets, etc.
# This feature applies to any table(s) OIDs using a common index
- targets:
Expand Down
19 changes: 15 additions & 4 deletions generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func generateConfig(nodes *Node, nameToNode map[string]*Node, logger log.Logger)
return fmt.Errorf("unable to determine absolute path for output")
}

content, err := os.ReadFile("generator.yml")
content, err := os.ReadFile(*generatorYmlPath)
if err != nil {
return fmt.Errorf("error reading yml config: %s", err)
}
Expand All @@ -50,7 +50,10 @@ func generateConfig(nodes *Node, nameToNode map[string]*Node, logger log.Logger)
outputConfig.Auths = cfg.Auths
outputConfig.Modules = make(map[string]*config.Module, len(cfg.Modules))
for name, m := range cfg.Modules {
level.Info(logger).Log("msg", "Generating config for module", "module", name)
err := level.Debug(logger).Log("msg", "Generating config for module", "module", name)
if err != nil {
return err
}
// Give each module a copy of the tree so that it can be modified.
mNodes := nodes.Copy()
// Build the map with new pointers.
Expand All @@ -65,7 +68,10 @@ func generateConfig(nodes *Node, nameToNode map[string]*Node, logger log.Logger)
}
outputConfig.Modules[name] = out
outputConfig.Modules[name].WalkParams = m.WalkParams
level.Info(logger).Log("msg", "Generated metrics", "module", name, "metrics", len(outputConfig.Modules[name].Metrics))
err = level.Debug(logger).Log("msg", "Generated metrics", "module", name, "metrics", len(outputConfig.Modules[name].Metrics))
if err != nil {
return err
}
}

config.DoNotHideSecrets = true
Expand All @@ -90,14 +96,19 @@ func generateConfig(nodes *Node, nameToNode map[string]*Node, logger log.Logger)
if err != nil {
return fmt.Errorf("error writing to output file: %s", err)
}
level.Info(logger).Log("msg", "Config written", "file", outputPath)
err = level.Debug(logger).Log("msg", "Config written", "file", outputPath)
if err != nil {
return err
}
return nil
}

var (
failOnParseErrors = kingpin.Flag("fail-on-parse-errors", "Exit with a non-zero status if there are MIB parsing errors").Default("false").Bool()
snmpMIBOpts = kingpin.Flag("snmp.mibopts", "Toggle various defaults controlling MIB parsing, see snmpwalk --help").String()
generateCommand = kingpin.Command("generate", "Generate snmp.yml from generator.yml")
userMibsDir = generateCommand.Flag("mibs-dir", "Path to mibs directory").Default("$HOME/.snmp/mibs:/usr/share/snmp/mibs:/usr/share/snmp/mibs/iana:/usr/share/snmp/mibs/ietf").Short('m').String()
generatorYmlPath = generateCommand.Flag("generator-path", "Path to the generator.yml file").Default("generator.yml").Short('g').String()
outputPath = generateCommand.Flag("output-path", "Path to to write resulting config file").Default("snmp.yml").Short('o').String()
parseErrorsCommand = kingpin.Command("parse_errors", "Debug: Print the parse errors output by NetSNMP")
dumpCommand = kingpin.Command("dump", "Debug: Dump the parsed and prepared MIBs")
Expand Down
37 changes: 31 additions & 6 deletions generator/net_snmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,24 +157,46 @@ var (
// Warning: This function plays with the stderr file descriptor.
func initSNMP(logger log.Logger) (string, error) {
// Load all the MIBs.
os.Setenv("MIBS", "ALL")
err := os.Setenv("MIBS", "ALL")
if err != nil {
return "", err
}
if *userMibsDir != "" {
err = level.Info(logger).Log("msg", "Loading MIBs from directories provided on command line")
if err != nil {
return "", err
}
C.netsnmp_set_mib_directory(C.CString(*userMibsDir))
}
// Help the user find their MIB directories.
level.Info(logger).Log("msg", "Loading MIBs", "from", C.GoString(C.netsnmp_get_mib_directory()))
err = level.Info(logger).Log("msg", "Loading MIBs", "from", C.GoString(C.netsnmp_get_mib_directory()))
if err != nil {
return "", err
}
if *snmpMIBOpts != "" {
C.snmp_mib_toggle_options(C.CString(*snmpMIBOpts))
}
// We want the descriptions.
C.snmp_set_save_descriptions(1)

// Make stderr go to a pipe, as netsnmp tends to spew a
// lot of errors on startup that there's no apparent
// way to disable or redirect.
r, w, err := os.Pipe()
if err != nil {
return "", fmt.Errorf("error creating pipe: %s", err)
}
defer r.Close()
defer w.Close()
defer func(r *os.File) {
err := r.Close()
if err != nil {

}
}(r)
defer func(w *os.File) {
err := w.Close()
if err != nil {

}
}(w)
savedStderrFd := C.dup(2)
C.close(2)
C.dup2(C.int(w.Fd()), 2)
Expand All @@ -194,7 +216,10 @@ func initSNMP(logger log.Logger) (string, error) {
C.netsnmp_init_mib()

// Restore stderr to normal.
w.Close()
err = w.Close()
if err != nil {
return "", err
}
C.close(2)
C.dup2(savedStderrFd, 2)
C.close(savedStderrFd)
Expand Down
20 changes: 16 additions & 4 deletions generator/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,10 @@ func generateConfigModule(cfg *ModuleConfig, node *Node, nameToNode map[string]*
// Find node to override.
n, ok := nameToNode[name]
if !ok {
level.Warn(logger).Log("msg", "Could not find node to override type", "node", name)
err := level.Warn(logger).Log("msg", "Could not find node to override type", "node", name)
if err != nil {
return nil, err
}
continue
}
// params.Type validated at generator configuration.
Expand Down Expand Up @@ -345,12 +348,18 @@ func generateConfigModule(cfg *ModuleConfig, node *Node, nameToNode map[string]*
index := &config.Index{Labelname: i}
indexNode, ok := nameToNode[i]
if !ok {
level.Warn(logger).Log("msg", "Could not find index for node", "node", n.Label, "index", i)
err := level.Warn(logger).Log("msg", "Could not find index for node", "node", n.Label, "index", i)
if err != nil {
return
}
return
}
index.Type, ok = metricType(indexNode.Type)
if !ok {
level.Warn(logger).Log("msg", "Can't handle index type on node", "node", n.Label, "index", i, "type", indexNode.Type)
err := level.Warn(logger).Log("msg", "Can't handle index type on node", "node", n.Label, "index", i, "type", indexNode.Type)
if err != nil {
return
}
return
}
index.FixedSize = indexNode.FixedSize
Expand All @@ -364,7 +373,10 @@ func generateConfigModule(cfg *ModuleConfig, node *Node, nameToNode map[string]*
if prevType == subtype {
metric.Indexes = metric.Indexes[:len(metric.Indexes)-1]
} else {
level.Warn(logger).Log("msg", "Can't handle index type on node, missing preceding", "node", n.Label, "type", index.Type, "missing", subtype)
err := level.Warn(logger).Log("msg", "Can't handle index type on node, missing preceding", "node", n.Label, "type", index.Type, "missing", subtype)
if err != nil {
return
}
return
}
}
Expand Down

0 comments on commit 3531baf

Please sign in to comment.