Skip to content

Commit

Permalink
Merge pull request #9 from Len4i/master
Browse files Browse the repository at this point in the history
add support for SRV records as host
  • Loading branch information
syepes authored Nov 15, 2021
2 parents 467f1af + a268477 commit c3b30a4
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 45 deletions.
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,38 @@ targets:
**Note:** Domain names are resolved (regularly) to their corresponding A and AAAA records (IPv4 and IPv6).
By default if not configured, `network_exporter` uses the system resolver to translate domain names to IP addresses.
You can alos override the DNS resolver address by specifying the `conf.nameserver` configuration setting.
You can also override the DNS resolver address by specifying the `conf.nameserver` configuration setting.

**SRV records:**
If host filed of a target contains SRV record of standard format `_<service>._<protocol>.<domain>`, it will be resolved and all it's A records will be added as separate targets with name and host of the this A record.
Every filed of parent target with SRV record will be inherited by sub targets except `name` and `host`

SRV record
```
_connectivity-check._icmp.example.com. 86400 IN SRV 10 5 8 server.example.com.
_connectivity-check._icmp.example.com. 86400 IN SRV 10 5 8 server2.example.com.
_connectivity-check._icmp.example.com. 86400 IN SRV 10 5 8 server3.example.com.
```
Configuration reference
```
- name: test-srv-record
host: _connectivity-check._icmp.example.com
type: ICMP
```
Will be resolved to 3 separate targets:
```
- name: server.example.com
host: server.example.com
type: ICMP
- name: server2.example.com
host: server.example.com
type: ICMP
- name: server3.example.com
host: server3.example.com
type: ICMP
```
## Deployment
Expand Down
132 changes: 88 additions & 44 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,51 @@ import (
)

// Config represents configuration for the exporter

type Targets []struct {
Name string `yaml:"name" json:"name"`
Host string `yaml:"host" json:"host"`
Type string `yaml:"type" json:"type"`
Proxy string `yaml:"proxy" json:"proxy"`
Probe []string `yaml:"probe" json:"probe"`
Labels extraKV `yaml:"labels,omitempty" json:"labels,omitempty"`
}

type HTTPGet struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
}

type TCP struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
}

type MTR struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
MaxHops int `yaml:"max-hops" json:"max-hops"`
Count int `yaml:"count" json:"count"`
}

type ICMP struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
Count int `yaml:"count" json:"count"`
}

type Conf struct {
Refresh duration `yaml:"refresh" json:"refresh"`
Nameserver string `yaml:"nameserver" json:"nameserver"`
}

type Config struct {
Conf struct {
Refresh duration `yaml:"refresh" json:"refresh"`
Nameserver string `yaml:"nameserver" json:"nameserver"`
} `yaml:"conf" json:"conf"`
ICMP struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
Count int `yaml:"count" json:"count"`
} `yaml:"icmp" json:"icmp"`
MTR struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
MaxHops int `yaml:"max-hops" json:"max-hops"`
Count int `yaml:"count" json:"count"`
} `yaml:"mtr" json:"mtr"`
TCP struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
} `yaml:"tcp" json:"tcp"`
HTTPGet struct {
Interval duration `yaml:"interval" json:"interval"`
Timeout duration `yaml:"timeout" json:"timeout"`
} `yaml:"http_get" json:"http_get"`
Targets []struct {
Name string `yaml:"name" json:"name"`
Host string `yaml:"host" json:"host"`
Type string `yaml:"type" json:"type"`
Proxy string `yaml:"proxy" json:"proxy"`
Probe []string `yaml:"probe" json:"probe"`
Labels extraKV `yaml:"labels,omitempty" json:"labels,omitempty"`
} `yaml:"targets" json:"targets"`
Conf `yaml:"conf" json:"conf"`
ICMP `yaml:"icmp" json:"icmp"`
MTR `yaml:"mtr" json:"mtr"`
TCP `yaml:"tcp" json:"tcp"`
HTTPGet `yaml:"http_get" json:"http_get"`
Targets `yaml:"targets" json:"targets"`
}

type duration time.Duration
Expand Down Expand Up @@ -83,24 +96,55 @@ func (sc *SafeConfig) ReloadConfig(confFile string) (err error) {
}

// Validate and Filter config
targets := c.Targets[:0]
targets := Targets{}
var targetNames []string

for _, t := range c.Targets {
targetNames = append(targetNames, t.Name)
found, _ := regexp.MatchString("^ICMP|MTR|ICMP+MTR|TCP|HTTPGet$", t.Type)
if found == false {
return fmt.Errorf("Target '%s' has unknown check type '%s' must be one of (ICMP|MTR|ICMP+MTR|TCP|HTTPGet)", t.Name, t.Type)
}
if common.SrvRecordCheck(t.Host) {
found, _ := regexp.MatchString("^ICMP|MTR|ICMP+MTR|TCP|HTTPGet$", t.Type)
if found == false {
return fmt.Errorf("Target '%s' has unknown check type '%s' must be one of (ICMP|MTR|ICMP+MTR|TCP|HTTPGet)", t.Name, t.Type)
}

srv_record_hosts, err := common.SrvRecordHosts(t.Host)
if err != nil {
return fmt.Errorf((fmt.Sprintf("Error processing SRV target: %s", t.Host)))
}

// Filter out the targets that are not assigned to the running host, if the `probe` is not specified don't filter
if t.Probe == nil {
targets = append(targets, t)
for _, srv_host := range srv_record_hosts {
targetNames = append(targetNames, srv_host)
sub_target := t
sub_target.Name = srv_host
sub_target.Host = srv_host

// Filter out the targets that are not assigned to the running host, if the `probe` is not specified don't filter
if sub_target.Probe == nil {
targets = append(targets, sub_target)
} else {
for _, p := range sub_target.Probe {
if p == hostname {
targets = append(targets, sub_target)
continue
}
}
}
}
} else {
for _, p := range t.Probe {
if p == hostname {
targets = append(targets, t)
continue
targetNames = append(targetNames, t.Name)
found, _ := regexp.MatchString("^ICMP|MTR|ICMP+MTR|TCP|HTTPGet$", t.Type)
if found == false {
return fmt.Errorf("Target '%s' has unknown check type '%s' must be one of (ICMP|MTR|ICMP+MTR|TCP|HTTPGet)", t.Name, t.Type)
}

// Filter out the targets that are not assigned to the running host, if the `probe` is not specified don't filter
if t.Probe == nil {
targets = append(targets, t)
} else {
for _, p := range t.Probe {
if p == hostname {
targets = append(targets, t)
continue
}
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions pkg/common/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,31 @@ import (
"fmt"
"math"
"net"
"strings"
"time"
)

func SrvRecordCheck(record string) bool {
record_split := strings.Split(record, ".")
if strings.HasPrefix(record_split[0], "_") && strings.HasPrefix(record_split[1], "_") {
return true
} else {
return false
}
}

func SrvRecordHosts(record string) ([]string, error) {
_, members, err := net.LookupSRV("", "", record)
if err != nil {
return nil, fmt.Errorf("Resolving target: %v", err)
}
hosts := []string{}
for _, host := range members {
hosts = append(hosts, host.Target)
}
return hosts, nil
}

// DestAddrs resolve the hostname to all it'ss IP's
func DestAddrs(host string, resolver *net.Resolver) ([]string, error) {
ipAddrs := make([]string, 0)
Expand Down

0 comments on commit c3b30a4

Please sign in to comment.