root/src/modules-lua/noit/module/ntp.lua

Revision d00b3d5de1f858f8204da9db700a44f9b103cae1, 6.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

Holy crap, ticket #4 is really old. Took all day to do this, but it seems
to work and is amazingly readable for what it implements. This version
doesn't upport measuring jitter which is substantially more complicated.

Enjoy!

refs #4

  • Property mode set to 100644
Line 
1 -- Copyright (c) 2010, OmniTI Computer Consulting, Inc.
2 -- All rights reserved.
3 --
4 -- Redistribution and use in source and binary forms, with or without
5 -- modification, are permitted provided that the following conditions are
6 -- met:
7 --
8 --     * Redistributions of source code must retain the above copyright
9 --       notice, this list of conditions and the following disclaimer.
10 --     * Redistributions in binary form must reproduce the above
11 --       copyright notice, this list of conditions and the following
12 --       disclaimer in the documentation and/or other materials provided
13 --       with the distribution.
14 --     * Neither the name OmniTI Computer Consulting, Inc. nor the names
15 --       of its contributors may be used to endorse or promote products
16 --       derived from this software without specific prior written
17 --       permission.
18 --
19 -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 -- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 -- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 -- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 module(..., package.seeall)
32
33 function onload(image)
34   image.xml_description([=[
35 <module>
36   <name>ntp</name>
37   <description><para>Determine clock skew from an NTP source.</para></description>
38   <loader>lua</loader>
39   <object>noit.module.ntp</object>
40   <moduleconfig />
41   <checkconfig />
42   <examples>
43     <example>
44       <title>Monitor an NTP service</title>
45       <para>The following example monitors an NTP services on 10.1.2.3.</para>
46       <programlisting><![CDATA[
47       <noit>
48         <modules>
49           <loader image="lua" name="lua">
50             <config><directory>/opt/reconnoiter/libexec/modules-lua/?.lua</directory></config>
51           </loader>
52           <module loader="lua" name="ntp" object="noit.module.ntp"/>
53         </modules>
54         <checks>
55           <check uuid="4ee1a1e2-1e60-11df-8e99-bf796ca462ef" module="ntp" target="10.1.2.3" period="60000" timeout="5000"/>
56         </checks>
57       </noit>
58       ]]></programlisting>
59     </example>
60   </examples>
61 </module>]=])
62   return 0
63 end
64
65 function init(module)
66   return 0
67 end
68
69 function config(module, options)
70   return 0
71 end
72
73 function elapsed(check, name, starttime, endtime)
74     local elapsedtime = endtime - starttime
75     local seconds = string.format('%.3f', noit.timeval.seconds(elapsedtime))
76     check.metric_uint32(name, math.floor(seconds * 1000 + 0.5))
77     return seconds
78 end
79
80 function timeval2ntp64(sec, usec)
81    -- packs a timeval into an NTP 64bit double
82    if(sec == 0 and usec == 0) then return string.pack('L', 0) end
83    local l32 = sec + 2208988800
84    local r32 = 4294.967296 * usec + 0.5
85    return string.pack('>II', l32, r32)
86 end
87
88 function ntp642timeval(s)
89   local cnt, l32, r32 = string.unpack(s, '>II')
90   local sec = l32 - 2208988800
91   local usec = (r32 - 0.5) / 4294.967296
92   return noit.timeval.new(sec, usec)
93 end
94
95 function double2ntp32(v)
96    local l16 = math.floor(v)
97    local r16 = 65536 * (v - l16)
98    return string.pack('>hH', l16, r16)
99 end
100
101 function ntp322double(s)
102    local cnt, l16, r16 = string.unpack(s, '>hH')
103    return l16 + (r16 / 65536)
104 end
105
106 function make_ntp_request(fin)
107     local f = fin or { }
108                              --    ALARM         V4      CLIENT
109     f.flags = f.flags or 227 -- (0x03 << 6) | (4 << 3) | 3
110     f.stratum = f.stratum or 0
111     f.poll = f.poll or 4
112     f.precision = f.precision or 250
113     f.rtdisp = f.rtdisp or 1
114     f.rtdelay = f.rtdelay or 1
115     f.refid = f.refid or 0
116     return string.pack('>bbcc', f.flags, f.stratum, f.poll, f.precision)
117         .. double2ntp32(f.rtdisp)
118         .. double2ntp32(f.rtdelay)
119         .. string.pack('>I', f.refid)
120         .. timeval2ntp64(0,0)
121         .. timeval2ntp64(0,0)
122         .. timeval2ntp64(0,0)
123         .. timeval2ntp64(noit.gettimeofday())
124 end
125
126 function decode_ntp_message(b)
127     local cnt
128     -- not as easy as a simple unpack
129     local ntp_hdr = string.sub(b,1,4)
130     local ntp_rtdelay = string.sub(b,5,8)
131     local ntp_rtdisp = string.sub(b,9,12)
132     local ntp_refid = string.sub(b,13,16)
133     local ntp_refts = string.sub(b,17,24)
134     local ntp_origts = string.sub(b,25,32)
135     local ntp_rxts = string.sub(b,33,40)
136     local ntp_txts = string.sub(b,41,48)
137     local r = { }
138     cnt, r.flags, r.stratum, r.poll, r.precision =
139         string.unpack(ntp_hdr, '>bbcc')
140     r.rtdelay = ntp322double(ntp_rtdelay)
141     r.rtdisp = ntp322double(ntp_rtdisp)
142     cnt, r.refid = string.unpack(ntp_refid, '>I')
143     r.refts = ntp642timeval(ntp_refts)
144     r.origts = ntp642timeval(ntp_origts)
145     r.rxts = ntp642timeval(ntp_rxts)
146     r.txts = ntp642timeval(ntp_txts)
147     return r
148 end
149
150 function calculate_offset(response, now)
151     local there_and = noit.timeval.seconds(response.rxts - response.origts)
152     local back_again = noit.timeval.seconds(response.txts - now)
153     return ( there_and + back_again ) / 2.0
154 end
155
156 function initiate(module, check)
157     local s = noit.socket('inet', 'udp')
158     local status = { }
159     local cnt = check.config.count or 4
160
161     check.unavailable()
162     check.bad()
163
164     s:connect(check.target, 123)
165     status.responses = 0
166     status.avg_offset = 0
167     status.offset = { }
168
169     for i = 1,cnt do
170         local req = make_ntp_request()
171         s:send(req)
172         local rv, s = s:recv(48)
173         local now = noit.timeval.now()
174         local response = decode_ntp_message(s)
175         local offset = calculate_offset(response, now)
176         if offset ~= nil then
177             table.insert(status.offset, offset)
178             status.avg_offset = status.avg_offset + offset
179             status.stratum = response.stratum
180             status.poll = math.pow(2, response.poll)
181             status.precision = math.pow(2, response.precision)
182             status.rtdisp = response.rtdisp
183             status.rtdelay = response.rtdelay
184             status.responses = status.responses + 1
185         end
186         noit.sleep(0.1)
187     end
188
189     status.avg_offset = status.avg_offset / # status.offset
190     check.status( cnt .. '/' .. status.responses )
191
192     if # status.offset > 0 then
193         check.metric_double('offset', status.avg_offset)
194         check.metric_uint32('requests', cnt)
195         check.metric_uint32('responses', status.responses)
196         check.metric_uint32('stratum', status.stratum)
197         check.metric_int32('poll', status.poll)
198         check.metric_double('precision', status.precision)
199         check.metric_double('rtdisp', status.rtdisp)
200         check.metric_double('rtdelay', status.rtdelay)
201         check.available()
202         check.good()
203     end
204 end
Note: See TracBrowser for help on using the browser.