Browse Source

Remove munin exporter

The exporter was never fully working and it turns out the protocol is
much harder to support than thought. Since we're not depending on munin
anyway, I've dropped it.
pull/2/merge
Johannes 'fish' Ziemke 11 years ago
parent
commit
cbd350a381
  1. 7
      exporter/exporter.go
  2. 236
      exporter/munin_collector.go

7
exporter/exporter.go

@ -85,12 +85,7 @@ func New(configFile string) (e exporter, err error) {
log.Fatalf("Couldn't attach collector: %s", err)
}
cm, err := NewMuninCollector(e.config, e.registry)
if err != nil {
log.Fatalf("Couldn't attach collector: %s", err)
}
e.collectors = []Collector{&cn, &cg, &cm}
e.collectors = []Collector{&cn, &cg}
if e.config.ListeningAddress != "" {
e.listeningAddress = e.config.ListeningAddress

236
exporter/munin_collector.go

@ -1,236 +0,0 @@
package exporter
import (
"bufio"
"fmt"
"github.com/prometheus/client_golang/prometheus"
"io"
"net"
"regexp"
"strconv"
"strings"
)
const (
muninAddress = "127.0.0.1:4949"
muninProto = "tcp"
)
var muninBanner = regexp.MustCompile(`# munin node at (.*)`)
type muninCollector struct {
name string
hostname string
graphs []string
gaugePerMetric map[string]prometheus.Gauge
config config
registry prometheus.Registry
connection net.Conn
}
// Takes a config struct and prometheus registry and returns a new Collector scraping munin.
func NewMuninCollector(config config, registry prometheus.Registry) (c muninCollector, err error) {
c = muninCollector{
name: "munin_collector",
config: config,
registry: registry,
gaugePerMetric: make(map[string]prometheus.Gauge),
}
return c, err
}
func (c *muninCollector) Name() string { return c.name }
func (c *muninCollector) connect() (err error) {
c.connection, err = net.Dial(muninProto, muninAddress)
if err != nil {
return err
}
debug(c.Name(), "Connected.")
reader := bufio.NewReader(c.connection)
head, err := reader.ReadString('\n')
if err != nil {
return err
}
matches := muninBanner.FindStringSubmatch(head)
if len(matches) != 2 { // expect: # munin node at <hostname>
return fmt.Errorf("Unexpected line: %s", head)
}
c.hostname = matches[1]
debug(c.Name(), "Found hostname: %s", c.hostname)
return err
}
func (c *muninCollector) muninCommand(cmd string) (reader *bufio.Reader, err error) {
if c.connection == nil {
err := c.connect()
if err != nil {
return reader, fmt.Errorf("Couldn't connect to munin: %s", err)
}
}
reader = bufio.NewReader(c.connection)
fmt.Fprintf(c.connection, cmd+"\n")
_, err = reader.Peek(1)
switch err {
case io.EOF:
debug(c.Name(), "not connected anymore, closing connection and reconnect.")
c.connection.Close()
err = c.connect()
if err != nil {
return reader, fmt.Errorf("Couldn't connect to %s: %s", muninAddress)
}
return c.muninCommand(cmd)
case nil: //no error
break
default:
return reader, fmt.Errorf("Unexpected error: %s", err)
}
return reader, err
}
func (c *muninCollector) muninList() (items []string, err error) {
munin, err := c.muninCommand("list")
if err != nil {
return items, fmt.Errorf("Couldn't get list: %s", err)
}
response, err := munin.ReadString('\n') // we are only interested in the first line
if err != nil {
return items, fmt.Errorf("Couldn't read response: %s", err)
}
if response[0] == '#' { // # not expected here
return items, fmt.Errorf("Error getting items: %s", response)
}
items = strings.Fields(strings.TrimRight(response, "\n"))
return items, err
}
func (c *muninCollector) muninConfig(name string) (config map[string]map[string]string, graphConfig map[string]string, err error) {
graphConfig = make(map[string]string)
config = make(map[string]map[string]string)
resp, err := c.muninCommand("config " + name)
if err != nil {
return config, graphConfig, fmt.Errorf("Couldn't get config for %s: %s", name, err)
}
for {
line, err := resp.ReadString('\n')
if err == io.EOF {
debug(c.Name(), "EOF, retrying")
return c.muninConfig(name)
}
if err != nil {
return nil, nil, err
}
if line == ".\n" { // munin end marker
break
}
if line[0] == '#' { // here it's just a comment, so ignore it
continue
}
parts := strings.Fields(line)
if len(parts) < 2 {
return nil, nil, fmt.Errorf("Line unexpected: %s", line)
}
key, value := parts[0], strings.TrimRight(strings.Join(parts[1:], " "), "\n")
key_parts := strings.Split(key, ".")
if len(key_parts) > 1 { // it's a metric config (metric.label etc)
if _, ok := config[key_parts[0]]; !ok {
config[key_parts[0]] = make(map[string]string)
}
config[key_parts[0]][key_parts[1]] = value
} else {
graphConfig[key_parts[0]] = value
}
}
return config, graphConfig, err
}
func (c *muninCollector) registerMetric(name string) (err error) {
configs, graphConfig, err := c.muninConfig(name)
if err != nil {
return fmt.Errorf("Couldn't get config for graph %s: %s", name, err)
}
for metric, config := range configs {
metricName := strings.Replace(name+"-"+metric, ".", "_", -1)
desc := graphConfig["graph_title"] + ": " + config["label"]
if config["info"] != "" {
desc = desc + ", " + config["info"]
}
gauge := prometheus.NewGauge()
debug(c.Name(), "Register %s: %s", metricName, desc)
c.gaugePerMetric[metricName] = gauge
c.registry.Register(metricName, desc, prometheus.NilLabels, gauge)
}
return nil
}
func (c *muninCollector) Update() (updates int, err error) {
items, err := c.muninList()
if err != nil {
return updates, fmt.Errorf("Couldn't get graph list: %s", err)
}
for _, name := range items {
c.graphs = append(c.graphs, name)
}
for _, graph := range c.graphs {
munin, err := c.muninCommand("fetch " + graph)
if err != nil {
return updates, err
}
for {
line, err := munin.ReadString('\n')
line = strings.TrimRight(line, "\n")
if err == io.EOF {
debug(c.Name(), "unexpected EOF, retrying")
return c.Update()
}
if err != nil {
return updates, err
}
if len(line) == 1 && line[0] == '.' {
break // end of list
}
parts := strings.Fields(line)
if len(parts) != 2 {
debug(c.Name(), "unexpected line: %s", line)
continue
}
key, value_s := strings.Split(parts[0], ".")[0], parts[1]
name := graph + "-" + key
if _, ok := c.gaugePerMetric[name]; !ok {
if err := c.registerMetric(name); err != nil {
return updates, err
}
}
value, err := strconv.ParseFloat(value_s, 64)
if err != nil {
debug(c.Name(), "Couldn't parse value in line %s, malformed?", line)
continue
}
labels := map[string]string{
"hostname": c.hostname,
}
debug(c.Name(), "Set %s{%s}: %f\n", name, labels, value)
c.gaugePerMetric[name].Set(labels, value)
updates++
}
}
return updates, err
}
Loading…
Cancel
Save