Skip to content

Commit bcbe2a5

Browse files
authored
Improve generator parse error handling (#1167)
Improve the handling of parse errors from net-snmp. * Fix the line split when there are no errors. * Default to failing on parse errors. * Add extra help messages to help when there are errors. * Improve `parse_errors` output. Signed-off-by: SuperQ <superq@gmail.com>
1 parent a55c2b6 commit bcbe2a5

File tree

1 file changed

+45
-14
lines changed

1 file changed

+45
-14
lines changed

generator/main.go

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"fmt"
1818
"os"
1919
"path/filepath"
20+
"regexp"
2021
"strings"
2122

2223
"github.com/alecthomas/kingpin/v2"
@@ -29,6 +30,10 @@ import (
2930
"github.com/prometheus/snmp_exporter/config"
3031
)
3132

33+
var (
34+
cannotFindModuleRE = regexp.MustCompile(`Cannot find module \((.+)\): (.+)`)
35+
)
36+
3237
// Generate a snmp_exporter config and write it out.
3338
func generateConfig(nodes *Node, nameToNode map[string]*Node, logger log.Logger) error {
3439
outputPath, err := filepath.Abs(*outputPath)
@@ -95,8 +100,8 @@ func generateConfig(nodes *Node, nameToNode map[string]*Node, logger log.Logger)
95100
}
96101

97102
var (
98-
failOnParseErrors = kingpin.Flag("fail-on-parse-errors", "Exit with a non-zero status if there are MIB parsing errors").Default("false").Bool()
99-
snmpMIBOpts = kingpin.Flag("snmp.mibopts", "Toggle various defaults controlling MIB parsing, see snmpwalk --help").String()
103+
failOnParseErrors = kingpin.Flag("fail-on-parse-errors", "Exit with a non-zero status if there are MIB parsing errors").Default("true").Bool()
104+
snmpMIBOpts = kingpin.Flag("snmp.mibopts", "Toggle various defaults controlling MIB parsing, see snmpwalk --help").Default("e").String()
100105
generateCommand = kingpin.Command("generate", "Generate snmp.yml from generator.yml")
101106
userMibsDir = kingpin.Flag("mibs-dir", "Paths to mibs directory").Default("").Short('m').Strings()
102107
generatorYmlPath = generateCommand.Flag("generator-path", "Path to the input generator.yml file").Default("generator.yml").Short('g').String()
@@ -112,30 +117,35 @@ func main() {
112117
command := kingpin.Parse()
113118
logger := promlog.New(promlogConfig)
114119

115-
parseOutput, err := initSNMP(logger)
120+
output, err := initSNMP(logger)
116121
if err != nil {
117122
level.Error(logger).Log("msg", "Error initializing netsnmp", "err", err)
118123
os.Exit(1)
119124
}
120125

121-
parseOutput = strings.TrimSpace(parseOutput)
122-
parseErrors := len(parseOutput) != 0
123-
if parseErrors {
124-
level.Warn(logger).Log("msg", "NetSNMP reported parse error(s)", "errors", len(strings.Split(parseOutput, "\n")))
125-
}
126+
parseOutput := scanParseOutput(logger, output)
127+
parseErrors := len(parseOutput)
126128

127129
nodes := getMIBTree()
128130
nameToNode := prepareTree(nodes, logger)
129131

130132
switch command {
131133
case generateCommand.FullCommand():
132-
err := generateConfig(nodes, nameToNode, logger)
133-
if err != nil {
134-
level.Error(logger).Log("msg", "Error generating config netsnmp", "err", err)
135-
os.Exit(1)
134+
if *failOnParseErrors && parseErrors > 0 {
135+
level.Error(logger).Log("msg", "Failing on reported parse error(s)", "help", "Use 'generator parse_errors' command to see errors, --no-fail-on-parse-errors to ignore")
136+
} else {
137+
err := generateConfig(nodes, nameToNode, logger)
138+
if err != nil {
139+
level.Error(logger).Log("msg", "Error generating config netsnmp", "err", err)
140+
os.Exit(1)
141+
}
136142
}
137143
case parseErrorsCommand.FullCommand():
138-
fmt.Println(parseOutput)
144+
if parseErrors > 0 {
145+
fmt.Printf("%s\n", strings.Join(parseOutput, "\n"))
146+
} else {
147+
level.Info(logger).Log("msg", "No parse errors")
148+
}
139149
case dumpCommand.FullCommand():
140150
walkNode(nodes, func(n *Node) {
141151
t := n.Type
@@ -150,7 +160,28 @@ func main() {
150160
n.Oid, n.Label, t, n.TextualConvention, n.Hint, n.Indexes, implied, n.EnumValues, n.Description)
151161
})
152162
}
153-
if *failOnParseErrors && parseErrors {
163+
if *failOnParseErrors && parseErrors > 0 {
154164
os.Exit(1)
155165
}
156166
}
167+
168+
func scanParseOutput(logger log.Logger, output string) []string {
169+
var parseOutput []string
170+
output = strings.TrimSpace(strings.ToValidUTF8(output, "�"))
171+
if len(output) > 0 {
172+
parseOutput = strings.Split(output, "\n")
173+
}
174+
parseErrors := len(parseOutput)
175+
176+
if parseErrors > 0 {
177+
level.Warn(logger).Log("msg", "NetSNMP reported parse error(s)", "errors", parseErrors)
178+
}
179+
180+
for _, line := range parseOutput {
181+
if strings.HasPrefix(line, "Cannot find module") {
182+
missing := cannotFindModuleRE.FindStringSubmatch(line)
183+
level.Error(logger).Log("msg", "Missing MIB", "mib", missing[1], "from", missing[2])
184+
}
185+
}
186+
return parseOutput
187+
}

0 commit comments

Comments
 (0)