config.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright (c) 2017 Arista Networks, Inc.
  2. // Use of this source code is governed by the Apache License 2.0
  3. // that can be found in the COPYING file.
  4. package main
  5. import (
  6. "fmt"
  7. "regexp"
  8. "strconv"
  9. "github.com/prometheus/client_golang/prometheus"
  10. "gopkg.in/yaml.v2"
  11. )
  12. // Config is the representation of ocprometheus's YAML config file.
  13. type Config struct {
  14. // Per-device labels.
  15. DeviceLabels map[string]prometheus.Labels
  16. // Prefixes to subscribe to.
  17. Subscriptions []string
  18. // Metrics to collect and how to munge them.
  19. Metrics []*MetricDef
  20. }
  21. // MetricDef is the representation of a metric definiton in the config file.
  22. type MetricDef struct {
  23. // Path is a regexp to match on the Update's full path.
  24. // The regexp must be a prefix match.
  25. // The regexp can define named capture groups to use as labels.
  26. Path string
  27. // Path compiled as a regexp.
  28. re *regexp.Regexp `deepequal:"ignore"`
  29. // Metric name.
  30. Name string
  31. // Metric help string.
  32. Help string
  33. // This map contains the metric descriptors for this metric for each device.
  34. devDesc map[string]*prometheus.Desc
  35. // This is the default metric descriptor for devices that don't have explicit descs.
  36. desc *prometheus.Desc
  37. }
  38. // Parses the config and creates the descriptors for each path and device.
  39. func parseConfig(cfg []byte) (*Config, error) {
  40. config := &Config{
  41. DeviceLabels: make(map[string]prometheus.Labels),
  42. }
  43. if err := yaml.Unmarshal(cfg, config); err != nil {
  44. return nil, fmt.Errorf("Failed to parse config: %v", err)
  45. }
  46. for _, def := range config.Metrics {
  47. def.re = regexp.MustCompile(def.Path)
  48. // Extract label names
  49. reNames := def.re.SubexpNames()[1:]
  50. labelNames := make([]string, len(reNames))
  51. for i, n := range reNames {
  52. labelNames[i] = n
  53. if n == "" {
  54. labelNames[i] = "unnamedLabel" + strconv.Itoa(i+1)
  55. }
  56. }
  57. // Create a default descriptor only if there aren't any per-device labels,
  58. // or if it's explicitly declared
  59. if len(config.DeviceLabels) == 0 || len(config.DeviceLabels["*"]) > 0 {
  60. def.desc = prometheus.NewDesc(def.Name, def.Help, labelNames, config.DeviceLabels["*"])
  61. }
  62. // Add per-device descriptors
  63. def.devDesc = make(map[string]*prometheus.Desc)
  64. for device, labels := range config.DeviceLabels {
  65. if device == "*" {
  66. continue
  67. }
  68. def.devDesc[device] = prometheus.NewDesc(def.Name, def.Help, labelNames, labels)
  69. }
  70. }
  71. return config, nil
  72. }
  73. // Returns the descriptor corresponding to the device and path, and labels extracted from the path.
  74. // If the device and path doesn't match any metrics, returns nil.
  75. func (c *Config) getDescAndLabels(s source) (*prometheus.Desc, []string) {
  76. for _, def := range c.Metrics {
  77. if groups := def.re.FindStringSubmatch(s.path); groups != nil {
  78. if desc, ok := def.devDesc[s.addr]; ok {
  79. return desc, groups[1:]
  80. }
  81. return def.desc, groups[1:]
  82. }
  83. }
  84. return nil, nil
  85. }
  86. // Sends all the descriptors to the channel.
  87. func (c *Config) getAllDescs(ch chan<- *prometheus.Desc) {
  88. for _, def := range c.Metrics {
  89. // Default descriptor might not be present
  90. if def.desc != nil {
  91. ch <- def.desc
  92. }
  93. for _, desc := range def.devDesc {
  94. ch <- desc
  95. }
  96. }
  97. }