checker-authoritative-consi.../checker/evaluate.go

206 lines
4.8 KiB
Go

package checker
import (
"encoding/json"
"fmt"
"strings"
"time"
sdk "git.happydns.org/checker-sdk-go/checker"
)
// Implements sdk.CheckerMetricsReporter.
func (p *authoritativeConsistencyProvider) ExtractMetrics(ctx sdk.ReportContext, collectedAt time.Time) ([]sdk.CheckMetric, error) {
var data ObservationData
if err := json.Unmarshal(ctx.Data(), &data); err != nil {
return nil, fmt.Errorf("checker: decoding observation: %w", err)
}
var out []sdk.CheckMetric
for name, r := range data.Results {
labels := map[string]string{"zone": data.Zone, "ns": name}
up := float64(0)
if r.UDPReachable && r.Authoritative {
up = 1
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_up",
Value: up,
Labels: labels,
Timestamp: collectedAt,
})
tcp := float64(0)
if r.TCPReachable {
tcp = 1
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_tcp",
Value: tcp,
Labels: labels,
Timestamp: collectedAt,
})
if r.LatencyMs > 0 {
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_latency_ms",
Value: float64(r.LatencyMs),
Unit: "ms",
Labels: labels,
Timestamp: collectedAt,
})
}
if r.Serial > 0 {
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_serial",
Value: float64(r.Serial),
Labels: labels,
Timestamp: collectedAt,
})
}
resolvable := float64(0)
if len(r.Addresses) > 0 {
resolvable = 1
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_resolvable",
Value: resolvable,
Labels: labels,
Timestamp: collectedAt,
})
// EDNS support is only meaningful once the server has actually answered.
if r.UDPReachable {
edns := float64(0)
if r.EDNSSupported {
edns = 1
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_edns",
Value: edns,
Labels: labels,
Timestamp: collectedAt,
})
}
}
zoneLabels := map[string]string{"zone": data.Zone}
uniqueSerials := map[uint32]struct{}{}
var minSerial, maxSerial uint32
serialSeen := false
for _, r := range data.Results {
if r == nil || !r.Authoritative || r.SOA == nil {
continue
}
uniqueSerials[r.Serial] = struct{}{}
if !serialSeen {
minSerial, maxSerial = r.Serial, r.Serial
serialSeen = true
continue
}
if r.Serial < minSerial {
minSerial = r.Serial
}
if r.Serial > maxSerial {
maxSerial = r.Serial
}
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_unique_serials",
Value: float64(len(uniqueSerials)),
Labels: zoneLabels,
Timestamp: collectedAt,
})
if data.HasSOA && data.DeclaredSerial > 0 {
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_serial",
Value: float64(data.DeclaredSerial),
Labels: zoneLabels,
Timestamp: collectedAt,
})
}
if serialSeen {
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_serial_spread",
Value: float64(maxSerial - minSerial),
Labels: zoneLabels,
Timestamp: collectedAt,
})
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_declared",
Value: float64(len(data.DeclaredNS)),
Labels: zoneLabels,
Timestamp: collectedAt,
})
reachable := 0
for _, r := range data.Results {
if r != nil && r.UDPReachable && r.Authoritative {
reachable++
}
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_reachable",
Value: float64(reachable),
Labels: zoneLabels,
Timestamp: collectedAt,
})
if data.ParentQueryError == "" && len(data.ParentNS) > 0 && len(data.DeclaredNS) > 0 {
missing, extra := diffStringSets(data.DeclaredNS, data.ParentNS)
match := float64(1)
if len(missing) > 0 || len(extra) > 0 {
match = 0
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_parent_delegation_match",
Value: match,
Labels: zoneLabels,
Timestamp: collectedAt,
})
}
rrsetGroups := map[string]struct{}{}
for _, r := range data.Results {
if r == nil || !r.Authoritative || len(r.NSRRset) == 0 {
continue
}
rrsetGroups[strings.Join(r.NSRRset, "|")] = struct{}{}
}
if len(rrsetGroups) > 0 {
v := float64(1)
if len(rrsetGroups) > 1 {
v = 0
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_ns_rrset_consistent",
Value: v,
Labels: zoneLabels,
Timestamp: collectedAt,
})
}
if data.HasSOA && serialSeen {
v := float64(1)
if len(collectSOAFieldsDrift(&data)) > 0 {
v = 0
}
out = append(out, sdk.CheckMetric{
Name: "authoritative_consistency_soa_fields_consistent",
Value: v,
Labels: zoneLabels,
Timestamp: collectedAt,
})
}
return out, nil
}