mirror of
				https://github.com/prometheus/node_exporter.git
				synced 2025-08-20 18:33:52 -07:00 
			
		
		
		
	Use the offset calculation that includes round trip time in the ntp collector
Previously the raw time difference was used which includes the network trip time between the node and the ntp server. This makes setting alerts off the value troublesome as it depends on the latency as well as the clock offset.
This commit is contained in:
		
							parent
							
								
									b93c7c394b
								
							
						
					
					
						commit
						387c64424b
					
				|  | @ -18,7 +18,6 @@ package collector | ||||||
| import ( | import ( | ||||||
| 	"flag" | 	"flag" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"time" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/beevik/ntp" | 	"github.com/beevik/ntp" | ||||||
| 	"github.com/prometheus/client_golang/prometheus" | 	"github.com/prometheus/client_golang/prometheus" | ||||||
|  | @ -58,13 +57,13 @@ func NewNtpCollector() (Collector, error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *ntpCollector) Update(ch chan<- prometheus.Metric) (err error) { | func (c *ntpCollector) Update(ch chan<- prometheus.Metric) (err error) { | ||||||
| 	t, err := ntp.TimeV(*ntpServer, byte(*ntpProtocolVersion)) | 	resp, err := ntp.Query(*ntpServer, *ntpProtocolVersion) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("couldn't get NTP drift: %s", err) | 		return fmt.Errorf("couldn't get NTP drift: %s", err) | ||||||
| 	} | 	} | ||||||
| 	drift := t.Sub(time.Now()) | 	driftSeconds := resp.ClockOffset.Seconds() | ||||||
| 	log.Debugf("Set ntp_drift_seconds: %f", drift.Seconds()) | 	log.Debugf("Set ntp_drift_seconds: %f", driftSeconds) | ||||||
| 	c.drift.Set(drift.Seconds()) | 	c.drift.Set(driftSeconds) | ||||||
| 	c.drift.Collect(ch) | 	c.drift.Collect(ch) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								vendor/github.com/beevik/ntp/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/beevik/ntp/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | Brett Vickers (beevik) | ||||||
|  | Mikhail Salosin (AlphaB) | ||||||
|  | Anton Tolchanov (knyar) | ||||||
|  | Christopher Batey (chbatey) | ||||||
							
								
								
									
										3
									
								
								vendor/github.com/beevik/ntp/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/beevik/ntp/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,3 +1,6 @@ | ||||||
|  | [](https://travis-ci.org/beevik/ntp) | ||||||
|  | [](https://godoc.org/github.com/beevik/ntp) | ||||||
|  | 
 | ||||||
| ntp | ntp | ||||||
| === | === | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										163
									
								
								vendor/github.com/beevik/ntp/ntp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										163
									
								
								vendor/github.com/beevik/ntp/ntp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -2,10 +2,11 @@ | ||||||
| // Use of this source code is governed by a BSD-style
 | // Use of this source code is governed by a BSD-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| // Package ntp provides a simple mechanism for querying the current time
 | // Package ntp provides a simple mechanism for querying the current time from
 | ||||||
| // from a remote NTP server.  This package only supports NTP client mode
 | // a remote NTP server.  This package only supports NTP client mode behavior
 | ||||||
| // behavior and version 4 of the NTP protocol.  See RFC 5905.
 | // and version 4 of the NTP protocol.  See RFC 5905. Approach inspired by go-
 | ||||||
| // Approach inspired by go-nuts post by Michael Hofmann:
 | // nuts post by Michael Hofmann:
 | ||||||
|  | //
 | ||||||
| // https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ
 | // https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/FlcdMU5fkLQ
 | ||||||
| package ntp | package ntp | ||||||
| 
 | 
 | ||||||
|  | @ -28,16 +29,43 @@ const ( | ||||||
| 	reservedPrivate | 	reservedPrivate | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	maxStratum = 16 | ||||||
|  | 	nanoPerSec = 1000000000 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	timeout  = 5 * time.Second | ||||||
|  | 	ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| type ntpTime struct { | type ntpTime struct { | ||||||
| 	Seconds  uint32 | 	Seconds  uint32 | ||||||
| 	Fraction uint32 | 	Fraction uint32 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t ntpTime) UTC() time.Time { | func (t ntpTime) Time() time.Time { | ||||||
| 	nsec := uint64(t.Seconds)*1e9 + (uint64(t.Fraction) * 1e9 >> 32) | 	return ntpEpoch.Add(t.sinceEpoch()) | ||||||
| 	return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nsec)) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // sinceEpoch converts the ntpTime record t into a duration since the NTP
 | ||||||
|  | // epoch time (Jan 1, 1900).
 | ||||||
|  | func (t ntpTime) sinceEpoch() time.Duration { | ||||||
|  | 	sec := time.Duration(t.Seconds) * time.Second | ||||||
|  | 	frac := time.Duration(uint64(t.Fraction) * nanoPerSec >> 32) | ||||||
|  | 	return sec + frac | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // toNtpTime converts the time value t into an ntpTime representation.
 | ||||||
|  | func toNtpTime(t time.Time) ntpTime { | ||||||
|  | 	nsec := uint64(t.Sub(ntpEpoch)) | ||||||
|  | 	return ntpTime{ | ||||||
|  | 		Seconds:  uint32(nsec / nanoPerSec), | ||||||
|  | 		Fraction: uint32((nsec % nanoPerSec) << 32 / nanoPerSec), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // msg is an internal representation of an NTP packet.
 | ||||||
| type msg struct { | type msg struct { | ||||||
| 	LiVnMode       byte // Leap Indicator (2) + Version (3) + Mode (3)
 | 	LiVnMode       byte // Leap Indicator (2) + Version (3) + Mode (3)
 | ||||||
| 	Stratum        byte | 	Stratum        byte | ||||||
|  | @ -45,69 +73,134 @@ type msg struct { | ||||||
| 	Precision      byte | 	Precision      byte | ||||||
| 	RootDelay      uint32 | 	RootDelay      uint32 | ||||||
| 	RootDispersion uint32 | 	RootDispersion uint32 | ||||||
| 	ReferenceId    uint32 | 	ReferenceID    uint32 | ||||||
| 	ReferenceTime  ntpTime | 	ReferenceTime  ntpTime | ||||||
| 	OriginTime     ntpTime | 	OriginTime     ntpTime | ||||||
| 	ReceiveTime    ntpTime | 	ReceiveTime    ntpTime | ||||||
| 	TransmitTime   ntpTime | 	TransmitTime   ntpTime | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetVersion sets the NTP protocol version on the message.
 | // setVersion sets the NTP protocol version on the message.
 | ||||||
| func (m *msg) SetVersion(v byte) { | func (m *msg) setVersion(v int) { | ||||||
| 	m.LiVnMode = (m.LiVnMode & 0xc7) | v<<3 | 	m.LiVnMode = (m.LiVnMode & 0xc7) | uint8(v)<<3 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetMode sets the NTP protocol mode on the message.
 | // setMode sets the NTP protocol mode on the message.
 | ||||||
| func (m *msg) SetMode(md mode) { | func (m *msg) setMode(md mode) { | ||||||
| 	m.LiVnMode = (m.LiVnMode & 0xf8) | byte(md) | 	m.LiVnMode = (m.LiVnMode & 0xf8) | byte(md) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Time returns the "receive time" from the remote NTP server
 | // A Response contains time data, some of which is returned by the NTP server
 | ||||||
| // specifed as host.  NTP client mode is used.
 | // and some of which is calculated by the client.
 | ||||||
| func getTime(host string, version byte) (time.Time, error) { | type Response struct { | ||||||
|  | 	Time        time.Time     // receive time reported by the server
 | ||||||
|  | 	RTT         time.Duration // round-trip time between client and server
 | ||||||
|  | 	ClockOffset time.Duration // local clock offset relative to server
 | ||||||
|  | 	Stratum     uint8         // stratum level of NTP server's clock
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Query returns information from the remote NTP server specifed as host.  NTP
 | ||||||
|  | // client mode is used.
 | ||||||
|  | func Query(host string, version int) (*Response, error) { | ||||||
|  | 	m, err := getTime(host, version) | ||||||
|  | 	now := toNtpTime(time.Now()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r := &Response{ | ||||||
|  | 		Time:        m.ReceiveTime.Time(), | ||||||
|  | 		RTT:         rtt(m.OriginTime, m.ReceiveTime, m.TransmitTime, now), | ||||||
|  | 		ClockOffset: offset(m.OriginTime, m.ReceiveTime, m.TransmitTime, now), | ||||||
|  | 		Stratum:     m.Stratum, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// https://tools.ietf.org/html/rfc5905#section-7.3
 | ||||||
|  | 	if r.Stratum == 0 { | ||||||
|  | 		r.Stratum = maxStratum | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return r, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Time returns the "receive time" from the remote NTP server specifed as
 | ||||||
|  | // host.  NTP client mode is used.
 | ||||||
|  | func getTime(host string, version int) (*msg, error) { | ||||||
| 	if version < 2 || version > 4 { | 	if version < 2 || version > 4 { | ||||||
| 		panic("ntp: invalid version number") | 		panic("ntp: invalid version number") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	raddr, err := net.ResolveUDPAddr("udp", host+":123") | 	raddr, err := net.ResolveUDPAddr("udp", host+":123") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return time.Now(), err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	con, err := net.DialUDP("udp", nil, raddr) | 	con, err := net.DialUDP("udp", nil, raddr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return time.Now(), err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	defer con.Close() | 	defer con.Close() | ||||||
| 	con.SetDeadline(time.Now().Add(5 * time.Second)) | 	con.SetDeadline(time.Now().Add(timeout)) | ||||||
| 
 | 
 | ||||||
| 	m := new(msg) | 	m := new(msg) | ||||||
| 	m.SetMode(client) | 	m.setMode(client) | ||||||
| 	m.SetVersion(version) | 	m.setVersion(version) | ||||||
|  | 	m.TransmitTime = toNtpTime(time.Now()) | ||||||
| 
 | 
 | ||||||
| 	err = binary.Write(con, binary.BigEndian, m) | 	err = binary.Write(con, binary.BigEndian, m) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return time.Now(), err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = binary.Read(con, binary.BigEndian, m) | 	err = binary.Read(con, binary.BigEndian, m) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return m, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TimeV returns the "receive time" from the remote NTP server specifed as
 | ||||||
|  | // host.  Use the NTP client mode with the requested version number (2, 3, or
 | ||||||
|  | // 4).
 | ||||||
|  | func TimeV(host string, version int) (time.Time, error) { | ||||||
|  | 	m, err := getTime(host, version) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return time.Now(), err | 		return time.Now(), err | ||||||
| 	} | 	} | ||||||
| 
 | 	return m.ReceiveTime.Time().Local(), nil | ||||||
| 	t := m.ReceiveTime.UTC().Local() |  | ||||||
| 	return t, nil |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TimeV returns the "receive time" from the remote NTP server
 | // Time returns the "receive time" from the remote NTP server specifed as
 | ||||||
| // specifed as host.  Use the NTP client mode with the requested
 | // host.  NTP client mode version 4 is used.
 | ||||||
| // version number (2, 3, or 4).
 |  | ||||||
| func TimeV(host string, version byte) (time.Time, error) { |  | ||||||
| 	return getTime(host, version) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Time returns the "receive time" from the remote NTP server
 |  | ||||||
| // specifed as host.  NTP client mode version 4 is used.
 |  | ||||||
| func Time(host string) (time.Time, error) { | func Time(host string) (time.Time, error) { | ||||||
| 	return getTime(host, 4) | 	return TimeV(host, 4) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func rtt(t1, t2, t3, t4 ntpTime) time.Duration { | ||||||
|  | 	// round trip delay time (https://tools.ietf.org/html/rfc5905#section-8)
 | ||||||
|  | 	//   T1 = client send time
 | ||||||
|  | 	//   T2 = server receive time
 | ||||||
|  | 	//   T3 = server reply time
 | ||||||
|  | 	//   T4 = client receive time
 | ||||||
|  | 	//
 | ||||||
|  | 	// RTT d:
 | ||||||
|  | 	//   d = (T4-T1) - (T3-T2)
 | ||||||
|  | 	a := t4.Time().Sub(t1.Time()) | ||||||
|  | 	b := t3.Time().Sub(t2.Time()) | ||||||
|  | 	return a - b | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func offset(t1, t2, t3, t4 ntpTime) time.Duration { | ||||||
|  | 	// local offset equation (https://tools.ietf.org/html/rfc5905#section-8)
 | ||||||
|  | 	//   T1 = client send time
 | ||||||
|  | 	//   T2 = server receive time
 | ||||||
|  | 	//   T3 = server reply time
 | ||||||
|  | 	//   T4 = client receive time
 | ||||||
|  | 	//
 | ||||||
|  | 	// Local clock offset t:
 | ||||||
|  | 	//   t = ((T2-T1) + (T3-T4)) / 2
 | ||||||
|  | 	a := t2.Time().Sub(t1.Time()) | ||||||
|  | 	b := t3.Time().Sub(t4.Time()) | ||||||
|  | 	return (a + b) / time.Duration(2) | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							|  | @ -8,9 +8,10 @@ | ||||||
| 			"revisionTime": "2016-01-18T19:00:32-05:00" | 			"revisionTime": "2016-01-18T19:00:32-05:00" | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | 			"checksumSHA1": "JhaYHdVIj52Fpdcb7DuDjr/gk0Q=", | ||||||
| 			"path": "github.com/beevik/ntp", | 			"path": "github.com/beevik/ntp", | ||||||
| 			"revision": "283ed9d548825a1dae0994311560e8dbf8efac68", | 			"revision": "f0545e6f2c3cb0d0a2ed115b88c539d8e5247ef3", | ||||||
| 			"revisionTime": "2015-11-09T15:30:19-08:00" | 			"revisionTime": "2016-05-31T23:09:58Z" | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"path": "github.com/beorn7/perks/quantile", | 			"path": "github.com/beorn7/perks/quantile", | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue