cmd/ntpcheck/checker/stats.go (78 lines of code) (raw):
/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package checker
import (
"math"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
// NTPStats is what we want to report as stats for FBAgent to put into ODS
type NTPStats struct {
PeerDelay float64 `json:"ntp.peer.delay"` // sys.peer delay in ms
PeerPoll int `json:"ntp.peer.poll"` // sys.peer poll in seconds
PeerJitter float64 `json:"ntp.peer.jitter"` // sys.peer jitter in ms
PeerOffset float64 `json:"ntp.peer.offset"` // sys.peer offset in ms
PeerStratum int `json:"ntp.peer.stratum"` // sys.peer stratum
Frequency float64 `json:"ntp.sys.frequency"` // clock frequency in PPM
StatError bool `json:"ntp.stat.error"` // error reported in Leap Status
Correction float64 `json:"ntp.correction"` // current correction
}
// NewNTPStats constructs NTPStats from NTPCheckResult
func NewNTPStats(r *NTPCheckResult) (*NTPStats, error) {
if r.SysVars == nil {
return nil, errors.New("no system variables to output stats")
}
syspeer, err := r.FindSysPeer()
var delay, jitter, offset float64
var poll uint
var stratum int
if err != nil {
log.Warningf("Can't get system peer: %v", err)
// calculate averages like ntp_stats_ods.py did
total := len(r.Peers)
if total == 0 {
return nil, errors.New("no peers detected to output stats")
}
goodPeers, err := r.FindGoodPeers()
if err != nil {
return nil, errors.Wrap(err, "nothing to calculate stats from")
}
totalDelay := 0.0
totalJitter := 0.0
totalOffset := 0.0
bestPPoll := 0
bestHPoll := 0
bestStratum := 0
for _, p := range goodPeers {
totalDelay += p.Delay
totalJitter += p.Jitter
totalOffset += p.Offset
if bestPPoll != 0 && p.PPoll < bestPPoll {
bestPPoll = p.PPoll
}
if bestHPoll != 0 && p.HPoll < bestHPoll {
bestHPoll = p.HPoll
}
if bestStratum != 0 && p.Stratum < bestStratum {
bestStratum = p.Stratum
}
}
delay = totalDelay / float64(total)
jitter = totalJitter / float64(total)
offset = totalOffset / float64(total)
stratum = bestStratum
poll = uint(math.Min(float64(bestPPoll), float64(bestHPoll)))
} else {
delay = syspeer.Delay
jitter = syspeer.Jitter
poll = uint(math.Min(float64(syspeer.PPoll), float64(syspeer.HPoll)))
stratum = syspeer.Stratum
offset = syspeer.Offset
}
output := NTPStats{
PeerDelay: delay,
PeerPoll: 1 << poll, // hpoll and ppoll are stored in seconds as a power of two
PeerJitter: jitter,
PeerOffset: offset,
PeerStratum: stratum,
Frequency: r.SysVars.Frequency,
Correction: r.Correction,
// that's how ntpstat defines unsynchronized
StatError: r.LI == 3,
}
return &output, nil
}