137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package checker
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// ExtractMetrics implements sdk.CheckerMetricsReporter. It consumes the raw
|
|
// observation, derives consensus on the fly, and emits time-series for
|
|
// dashboards. Severity counters are computed from the rule states carried
|
|
// in the ReportContext rather than re-derived from raw data.
|
|
func (p *resolverPropagationProvider) ExtractMetrics(ctx sdk.ReportContext, collectedAt time.Time) ([]sdk.CheckMetric, error) {
|
|
var data ResolverPropagationData
|
|
if err := json.Unmarshal(ctx.Data(), &data); err != nil {
|
|
return nil, fmt.Errorf("resolver-propagation: decoding observation: %w", err)
|
|
}
|
|
deriveView(&data)
|
|
|
|
var out []sdk.CheckMetric
|
|
zone := data.Zone
|
|
|
|
rollups := []struct {
|
|
name string
|
|
val float64
|
|
}{
|
|
{"resolver_propagation_resolvers_total", float64(data.Stats.TotalResolvers)},
|
|
{"resolver_propagation_resolvers_reachable", float64(data.Stats.ReachableResolvers)},
|
|
{"resolver_propagation_unfiltered_agreeing", float64(data.Stats.UnfilteredAgreeing)},
|
|
{"resolver_propagation_regions_covered", float64(data.Stats.CountriesCovered)},
|
|
{"resolver_propagation_run_duration_ms", float64(data.RunDurationMs)},
|
|
}
|
|
for _, r := range rollups {
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: r.name,
|
|
Value: r.val,
|
|
Labels: map[string]string{"zone": zone},
|
|
Timestamp: collectedAt,
|
|
})
|
|
}
|
|
|
|
if data.DeclaredSerial != 0 {
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: "resolver_propagation_declared_serial",
|
|
Value: float64(data.DeclaredSerial),
|
|
Labels: map[string]string{"zone": zone},
|
|
Timestamp: collectedAt,
|
|
})
|
|
}
|
|
|
|
soaKey := rrsetKey(zone, "SOA")
|
|
var staleResolvers int
|
|
for id, rv := range data.Resolvers {
|
|
if rv.Filtered {
|
|
continue
|
|
}
|
|
p := rv.Probes[soaKey]
|
|
if p == nil || p.Error != "" || p.Rcode != "NOERROR" {
|
|
continue
|
|
}
|
|
s := extractSerial(p.Records)
|
|
if s == 0 {
|
|
continue
|
|
}
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: "resolver_propagation_observed_serial",
|
|
Value: float64(s),
|
|
Labels: map[string]string{
|
|
"zone": zone,
|
|
"resolver": id,
|
|
},
|
|
Timestamp: collectedAt,
|
|
})
|
|
if data.DeclaredSerial != 0 && s < data.DeclaredSerial {
|
|
staleResolvers++
|
|
}
|
|
}
|
|
if data.DeclaredSerial != 0 {
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: "resolver_propagation_serial_drift_resolvers",
|
|
Value: float64(staleResolvers),
|
|
Labels: map[string]string{"zone": zone},
|
|
Timestamp: collectedAt,
|
|
})
|
|
}
|
|
|
|
for id, rv := range data.Resolvers {
|
|
labels := map[string]string{
|
|
"zone": zone,
|
|
"resolver": id,
|
|
"ip": rv.IP,
|
|
"region": rv.Region,
|
|
"transport": string(rv.Transport),
|
|
}
|
|
|
|
up := float64(0)
|
|
if rv.Reachable {
|
|
up = 1
|
|
}
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: "resolver_propagation_resolver_up",
|
|
Value: up, Labels: labels, Timestamp: collectedAt,
|
|
})
|
|
|
|
var total, n int64
|
|
for _, p := range rv.Probes {
|
|
if p.Error != "" {
|
|
continue
|
|
}
|
|
total += p.LatencyMs
|
|
n++
|
|
}
|
|
if n > 0 {
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: "resolver_propagation_resolver_latency_ms",
|
|
Value: float64(total) / float64(n), Unit: "ms",
|
|
Labels: labels, Timestamp: collectedAt,
|
|
})
|
|
}
|
|
}
|
|
|
|
for key, v := range data.RRsets {
|
|
out = append(out, sdk.CheckMetric{
|
|
Name: "resolver_propagation_rrset_signatures",
|
|
Value: float64(len(v.Groups)),
|
|
Labels: map[string]string{
|
|
"zone": zone,
|
|
"rrset": key,
|
|
},
|
|
Timestamp: collectedAt,
|
|
})
|
|
}
|
|
|
|
return out, nil
|
|
}
|