123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- // Copyright (c) 2017 Arista Networks, Inc.
- // Use of this source code is governed by the Apache License 2.0
- // that can be found in the COPYING file.
- package main
- import (
- "fmt"
- "regexp"
- "strconv"
- "github.com/prometheus/client_golang/prometheus"
- "gopkg.in/yaml.v2"
- )
- // Config is the representation of ocprometheus's YAML config file.
- type Config struct {
- // Per-device labels.
- DeviceLabels map[string]prometheus.Labels
- // Prefixes to subscribe to.
- Subscriptions []string
- // Metrics to collect and how to munge them.
- Metrics []*MetricDef
- }
- // MetricDef is the representation of a metric definiton in the config file.
- type MetricDef struct {
- // Path is a regexp to match on the Update's full path.
- // The regexp must be a prefix match.
- // The regexp can define named capture groups to use as labels.
- Path string
- // Path compiled as a regexp.
- re *regexp.Regexp `deepequal:"ignore"`
- // Metric name.
- Name string
- // Metric help string.
- Help string
- // This map contains the metric descriptors for this metric for each device.
- devDesc map[string]*prometheus.Desc
- // This is the default metric descriptor for devices that don't have explicit descs.
- desc *prometheus.Desc
- }
- // Parses the config and creates the descriptors for each path and device.
- func parseConfig(cfg []byte) (*Config, error) {
- config := &Config{
- DeviceLabels: make(map[string]prometheus.Labels),
- }
- if err := yaml.Unmarshal(cfg, config); err != nil {
- return nil, fmt.Errorf("Failed to parse config: %v", err)
- }
- for _, def := range config.Metrics {
- def.re = regexp.MustCompile(def.Path)
- // Extract label names
- reNames := def.re.SubexpNames()[1:]
- labelNames := make([]string, len(reNames))
- for i, n := range reNames {
- labelNames[i] = n
- if n == "" {
- labelNames[i] = "unnamedLabel" + strconv.Itoa(i+1)
- }
- }
- // Create a default descriptor only if there aren't any per-device labels,
- // or if it's explicitly declared
- if len(config.DeviceLabels) == 0 || len(config.DeviceLabels["*"]) > 0 {
- def.desc = prometheus.NewDesc(def.Name, def.Help, labelNames, config.DeviceLabels["*"])
- }
- // Add per-device descriptors
- def.devDesc = make(map[string]*prometheus.Desc)
- for device, labels := range config.DeviceLabels {
- if device == "*" {
- continue
- }
- def.devDesc[device] = prometheus.NewDesc(def.Name, def.Help, labelNames, labels)
- }
- }
- return config, nil
- }
- // Returns the descriptor corresponding to the device and path, and labels extracted from the path.
- // If the device and path doesn't match any metrics, returns nil.
- func (c *Config) getDescAndLabels(s source) (*prometheus.Desc, []string) {
- for _, def := range c.Metrics {
- if groups := def.re.FindStringSubmatch(s.path); groups != nil {
- if desc, ok := def.devDesc[s.addr]; ok {
- return desc, groups[1:]
- }
- return def.desc, groups[1:]
- }
- }
- return nil, nil
- }
- // Sends all the descriptors to the channel.
- func (c *Config) getAllDescs(ch chan<- *prometheus.Desc) {
- for _, def := range c.Metrics {
- // Default descriptor might not be present
- if def.desc != nil {
- ch <- def.desc
- }
- for _, desc := range def.devDesc {
- ch <- desc
- }
- }
- }
|