riemann-tools 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.github/workflows/ci.yml +13 -0
  4. data/.github/workflows/codeql-analysis.yml +72 -0
  5. data/.rubocop.yml +32 -0
  6. data/CHANGELOG.md +31 -2
  7. data/README.markdown +8 -24
  8. data/Rakefile +4 -2
  9. data/SECURITY.md +42 -0
  10. data/bin/riemann-apache-status +92 -78
  11. data/bin/riemann-bench +54 -49
  12. data/bin/riemann-cloudant +44 -40
  13. data/bin/riemann-consul +82 -76
  14. data/bin/riemann-dir-files-count +53 -47
  15. data/bin/riemann-dir-space +53 -47
  16. data/bin/riemann-diskstats +78 -75
  17. data/bin/riemann-fd +68 -48
  18. data/bin/riemann-freeswitch +108 -103
  19. data/bin/riemann-haproxy +46 -40
  20. data/bin/riemann-health +4 -343
  21. data/bin/riemann-kvminstance +18 -13
  22. data/bin/riemann-memcached +35 -29
  23. data/bin/riemann-net +4 -104
  24. data/bin/riemann-nginx-status +74 -67
  25. data/bin/riemann-ntp +4 -33
  26. data/bin/riemann-portcheck +40 -31
  27. data/bin/riemann-proc +96 -90
  28. data/bin/riemann-varnish +51 -45
  29. data/bin/riemann-zookeeper +38 -34
  30. data/lib/riemann/tools/health.rb +347 -0
  31. data/lib/riemann/tools/net.rb +104 -0
  32. data/lib/riemann/tools/ntp.rb +41 -0
  33. data/lib/riemann/tools/version.rb +1 -1
  34. data/lib/riemann/tools.rb +37 -40
  35. data/riemann-tools.gemspec +4 -1
  36. data/tools/riemann-aws/{Rakefile.rb → Rakefile} +2 -0
  37. data/tools/riemann-aws/bin/riemann-aws-billing +72 -66
  38. data/tools/riemann-aws/bin/riemann-aws-rds-status +55 -41
  39. data/tools/riemann-aws/bin/riemann-aws-sqs-status +37 -31
  40. data/tools/riemann-aws/bin/riemann-aws-status +63 -51
  41. data/tools/riemann-aws/bin/riemann-elb-metrics +149 -148
  42. data/tools/riemann-aws/bin/riemann-s3-list +70 -65
  43. data/tools/riemann-aws/bin/riemann-s3-status +85 -82
  44. data/tools/riemann-chronos/{Rakefile.rb → Rakefile} +2 -0
  45. data/tools/riemann-chronos/bin/riemann-chronos +136 -119
  46. data/tools/riemann-docker/{Rakefile.rb → Rakefile} +2 -0
  47. data/tools/riemann-docker/bin/riemann-docker +163 -174
  48. data/tools/riemann-elasticsearch/{Rakefile.rb → Rakefile} +2 -0
  49. data/tools/riemann-elasticsearch/bin/riemann-elasticsearch +155 -147
  50. data/tools/riemann-marathon/{Rakefile.rb → Rakefile} +2 -0
  51. data/tools/riemann-marathon/bin/riemann-marathon +138 -122
  52. data/tools/riemann-mesos/{Rakefile.rb → Rakefile} +2 -0
  53. data/tools/riemann-mesos/bin/riemann-mesos +125 -110
  54. data/tools/riemann-munin/{Rakefile.rb → Rakefile} +2 -0
  55. data/tools/riemann-munin/bin/riemann-munin +28 -22
  56. data/tools/riemann-rabbitmq/{Rakefile.rb → Rakefile} +2 -0
  57. data/tools/riemann-rabbitmq/bin/riemann-rabbitmq +226 -222
  58. data/tools/riemann-riak/{Rakefile.rb → Rakefile} +2 -0
  59. data/tools/riemann-riak/bin/riemann-riak +281 -289
  60. data/tools/riemann-riak/riak_status/riak_status.rb +39 -39
  61. metadata +65 -16
@@ -1,41 +1,45 @@
1
1
  #!/usr/bin/env ruby
2
- Process.setproctitle($0)
2
+ # frozen_string_literal: true
3
+
4
+ Process.setproctitle($PROGRAM_NAME)
3
5
 
4
6
  # Gathers zookeeper STATS and submits them to Riemann.
5
7
 
6
- require File.expand_path('../../lib/riemann/tools', __FILE__)
7
-
8
- class Riemann::Tools::Zookeeper
9
- include Riemann::Tools
10
- require 'socket'
11
-
12
- opt :zookeeper_host, "Zookeeper hostname", :default => 'localhost'
13
- opt :zookeeper_port, "Zookeeper port", :default => 2181
14
-
15
- def tick
16
- sock = TCPSocket.new(opts[:zookeeper_host], opts[:zookeeper_port])
17
- sock.sync = true
18
- sock.print("mntr")
19
- sock.flush
20
-
21
-
22
- data = {}
23
- while true
24
- stats = sock.gets
25
-
26
- break if stats.nil?
27
-
28
- m = stats.match /^(\w+)\t+(.*)/
29
-
30
- report(
31
- :host => opts[ :zookeeper_host].dup,
32
- :service => "zookeeper #{m[1]}",
33
- :metric => m[2].to_f,
34
- :state => 'ok',
35
- :tags => ['zookeeper']
36
- )
37
- end
38
- sock.close
8
+ require File.expand_path('../lib/riemann/tools', __dir__)
9
+
10
+ module Riemann
11
+ module Tools
12
+ class Zookeeper
13
+ include Riemann::Tools
14
+ require 'socket'
15
+
16
+ opt :zookeeper_host, 'Zookeeper hostname', default: 'localhost'
17
+ opt :zookeeper_port, 'Zookeeper port', default: 2181
18
+
19
+ def tick
20
+ sock = TCPSocket.new(opts[:zookeeper_host], opts[:zookeeper_port])
21
+ sock.sync = true
22
+ sock.print('mntr')
23
+ sock.flush
24
+
25
+ loop do
26
+ stats = sock.gets
27
+
28
+ break if stats.nil?
29
+
30
+ m = stats.match(/^(\w+)\t+(.*)/)
31
+
32
+ report(
33
+ host: opts[:zookeeper_host].dup,
34
+ service: "zookeeper #{m[1]}",
35
+ metric: m[2].to_f,
36
+ state: 'ok',
37
+ tags: ['zookeeper'],
38
+ )
39
+ end
40
+ sock.close
41
+ end
42
+ end
39
43
  end
40
44
  end
41
45
 
@@ -0,0 +1,347 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'riemann/tools'
4
+ require 'riemann/tools/utils'
5
+
6
+ module Riemann
7
+ module Tools
8
+ class Health
9
+ include Riemann::Tools
10
+ include Riemann::Tools::Utils
11
+
12
+ opt :cpu_warning, 'CPU warning threshold (fraction of total jiffies)', default: 0.9
13
+ opt :cpu_critical, 'CPU critical threshold (fraction of total jiffies)', default: 0.95
14
+ opt :disk_warning, 'Disk warning threshold (fraction of space used)', default: 0.9
15
+ opt :disk_critical, 'Disk critical threshold (fraction of space used)', default: 0.95
16
+ opt :disk_ignorefs, 'A list of filesystem types to ignore',
17
+ default: %w[anon_inodefs autofs cd9660 devfs devtmpfs fdescfs iso9660 linprocfs linsysfs nfs procfs tmpfs]
18
+ opt :load_warning, 'Load warning threshold (load average / core)', default: 3.0
19
+ opt :load_critical, 'Load critical threshold (load average / core)', default: 8.0
20
+ opt :memory_warning, 'Memory warning threshold (fraction of RAM)', default: 0.85
21
+ opt :memory_critical, 'Memory critical threshold (fraction of RAM)', default: 0.95
22
+ opt :checks, 'A list of checks to run.', type: :strings, default: %w[cpu load memory disk]
23
+
24
+ def initialize
25
+ @limits = {
26
+ cpu: { critical: opts[:cpu_critical], warning: opts[:cpu_warning] },
27
+ disk: { critical: opts[:disk_critical], warning: opts[:disk_warning] },
28
+ load: { critical: opts[:load_critical], warning: opts[:load_warning] },
29
+ memory: { critical: opts[:memory_critical], warning: opts[:memory_warning] },
30
+ }
31
+ case (@ostype = `uname -s`.chomp.downcase)
32
+ when 'darwin'
33
+ @cores = `sysctl -n hw.ncpu`.to_i
34
+ @cpu = method :darwin_cpu
35
+ @disk = method :disk
36
+ @load = method :darwin_load
37
+ @memory = method :darwin_memory
38
+ darwin_top
39
+ when 'freebsd'
40
+ @cores = `sysctl -n hw.ncpu`.to_i
41
+ @cpu = method :freebsd_cpu
42
+ @disk = method :disk
43
+ @load = method :bsd_load
44
+ @memory = method :freebsd_memory
45
+ when 'openbsd'
46
+ @cores = `sysctl -n hw.ncpu`.to_i
47
+ @cpu = method :openbsd_cpu
48
+ @disk = method :disk
49
+ @load = method :bsd_load
50
+ @memory = method :openbsd_memory
51
+ when 'sunos'
52
+ @cores = `mpstat -a 2>/dev/null`.split[33].to_i
53
+ @cpu = method :sunos_cpu
54
+ @disk = method :disk
55
+ @load = method :bsd_load
56
+ @memory = method :sunos_memory
57
+ else
58
+ @cores = `nproc`.to_i
59
+ puts "WARNING: OS '#{@ostype}' not explicitly supported. Falling back to Linux" unless @ostype == 'linux'
60
+ @cpu = method :linux_cpu
61
+ @disk = method :disk
62
+ @load = method :linux_load
63
+ @memory = method :linux_memory
64
+ @supports_exclude_type = `df --help 2>&1 | grep -e "--exclude-type"` != ''
65
+ end
66
+
67
+ opts[:checks].each do |check|
68
+ case check
69
+ when 'disk'
70
+ @disk_enabled = true
71
+ when 'load'
72
+ @load_enabled = true
73
+ when 'cpu'
74
+ @cpu_enabled = true
75
+ when 'memory'
76
+ @memory_enabled = true
77
+ end
78
+ end
79
+ end
80
+
81
+ def alert(service, state, metric, description)
82
+ report(
83
+ service: service.to_s,
84
+ state: state.to_s,
85
+ metric: metric.to_f,
86
+ description: description,
87
+ )
88
+ end
89
+
90
+ def report_pct(service, fraction, report)
91
+ return unless fraction
92
+
93
+ if fraction > @limits[service][:critical]
94
+ alert service, :critical, fraction, "#{format('%.2f', fraction * 100)}% #{report}"
95
+ elsif fraction > @limits[service][:warning]
96
+ alert service, :warning, fraction, "#{format('%.2f', fraction * 100)}% #{report}"
97
+ else
98
+ alert service, :ok, fraction, "#{format('%.2f', fraction * 100)}% #{report}"
99
+ end
100
+ end
101
+
102
+ def linux_cpu
103
+ new = File.read('/proc/stat')
104
+ unless new[/cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/]
105
+ alert 'cpu', :unknown, nil, "/proc/stat doesn't include a CPU line"
106
+ return false
107
+ end
108
+ u2, n2, s2, i2 = [Regexp.last_match(1), Regexp.last_match(2), Regexp.last_match(3),
109
+ Regexp.last_match(4),].map(&:to_i)
110
+
111
+ if @old_cpu
112
+ u1, n1, s1, i1 = @old_cpu
113
+
114
+ used = (u2 + n2 + s2) - (u1 + n1 + s1)
115
+ total = used + i2 - i1
116
+ fraction = used.to_f / total
117
+
118
+ report_pct :cpu, fraction, "user+nice+system\n\n#{reverse_numeric_sort_with_header(`ps -eo pcpu,pid,comm`)}"
119
+ end
120
+
121
+ @old_cpu = [u2, n2, s2, i2]
122
+ end
123
+
124
+ def linux_load
125
+ load = File.read('/proc/loadavg').split(/\s+/)[0].to_f / @cores
126
+ if load > @limits[:load][:critical]
127
+ alert 'load', :critical, load, "1-minute load average/core is #{load}"
128
+ elsif load > @limits[:load][:warning]
129
+ alert 'load', :warning, load, "1-minute load average/core is #{load}"
130
+ else
131
+ alert 'load', :ok, load, "1-minute load average/core is #{load}"
132
+ end
133
+ end
134
+
135
+ def linux_memory
136
+ m = File.read('/proc/meminfo').split(/\n/).each_with_object({}) do |line, info|
137
+ x = line.split(/:?\s+/)
138
+ # Assume kB...
139
+ info[x[0]] = x[1].to_i
140
+ end
141
+
142
+ free = m['MemFree'].to_i + m['Buffers'].to_i + m['Cached'].to_i
143
+ total = m['MemTotal'].to_i
144
+ fraction = 1 - (free.to_f / total)
145
+
146
+ report_pct :memory, fraction, "used\n\n#{reverse_numeric_sort_with_header(`ps -eo pmem,pid,comm`)}"
147
+ end
148
+
149
+ def freebsd_cpu
150
+ u2, n2, s2, t2, i2 = `sysctl -n kern.cp_time 2>/dev/null`.split.map(&:to_i) # FreeBSD has 5 cpu stats
151
+
152
+ if @old_cpu
153
+ u1, n1, s1, t1, i1 = @old_cpu
154
+
155
+ used = (u2 + n2 + s2 + t2) - (u1 + n1 + s1 + t1)
156
+ total = used + i2 - i1
157
+ fraction = used.to_f / total
158
+
159
+ report_pct :cpu, fraction,
160
+ "user+nice+sytem+interrupt\n\n#{reverse_numeric_sort_with_header(`ps -axo pcpu,pid,comm`)}"
161
+ end
162
+
163
+ @old_cpu = [u2, n2, s2, t2, i2]
164
+ end
165
+
166
+ def openbsd_cpu
167
+ u2, n2, s2, t2, i2 = # OpenBSD separates with ,
168
+ `sysctl -n kern.cp_time 2>/dev/null`.split(',').map(&:to_i)
169
+ if @old_cpu
170
+ u1, n1, s1, t1, i1 = @old_cpu
171
+
172
+ used = (u2 + n2 + s2 + t2) - (u1 + n1 + s1 + t1)
173
+ total = used + i2 - i1
174
+ fraction = used.to_f / total
175
+
176
+ report_pct :cpu, fraction,
177
+ "user+nice+sytem+interrupt\n\n#{reverse_numeric_sort_with_header(`ps -axo pcpu,pid,comm`)}"
178
+ end
179
+
180
+ @old_cpu = [u2, n2, s2, t2, i2]
181
+ end
182
+
183
+ def sunos_cpu
184
+ mpstats = `mpstat -a 2>/dev/null`.split
185
+ u2 = mpstats[29].to_i
186
+ s2 = mpstats[30].to_i
187
+ t2 = mpstats[31].to_i
188
+ i2 = mpstats[32].to_i
189
+
190
+ if @old_cpu
191
+ u1, s1, t1, i1 = @old_cpu
192
+
193
+ used = (u2 + s2 + t2) - (u1 + s1 + t1)
194
+ total = used + i2 - i1
195
+ fraction = if i2 == i1 && used.zero? # If the system is <1% used in both samples then total will be 0 + (99 - 99), avoid a div by 0
196
+ 0
197
+ else
198
+ used.to_f / total
199
+ end
200
+
201
+ report_pct :cpu, fraction,
202
+ "user+sytem+interrupt\n\n#{reverse_numeric_sort_with_header(`ps -ao pcpu,pid,comm`)}"
203
+ end
204
+
205
+ @old_cpu = [u2, s2, t2, i2]
206
+ end
207
+
208
+ def bsd_load
209
+ m = `uptime`.split(':')[-1].chomp.gsub(/\s+/, '').split(',')
210
+ load = m[0].to_f / @cores
211
+ if load > @limits[:load][:critical]
212
+ alert 'load', :critical, load, "1-minute load average/core is #{load}"
213
+ elsif load > @limits[:load][:warning]
214
+ alert 'load', :warning, load, "1-minute load average/core is #{load}"
215
+ else
216
+ alert 'load', :ok, load, "1-minute load average/core is #{load}"
217
+ end
218
+ end
219
+
220
+ def freebsd_memory
221
+ meminfo = `sysctl -n vm.stats.vm.v_page_count vm.stats.vm.v_wire_count vm.stats.vm.v_active_count 2>/dev/null`.chomp.split
222
+ fraction = (meminfo[1].to_f + meminfo[2].to_f) / meminfo[0].to_f
223
+
224
+ report_pct :memory, fraction, "used\n\n#{reverse_numeric_sort_with_header(`ps -axo pmem,pid,comm`)}"
225
+ end
226
+
227
+ def openbsd_memory
228
+ meminfo = `vmstat 2>/dev/null`.chomp.split
229
+ fraction = meminfo[28].to_f / meminfo[29] # The ratio of active to free memory unlike the others :(
230
+
231
+ report_pct :memory, fraction, "used\n\n#{reverse_numeric_sort_with_header(`ps -axo pmem,pid,comm`)}"
232
+ end
233
+
234
+ def sunos_memory
235
+ meminfo = `vmstat 2>/dev/null`.chomp.split
236
+ total_mem = `prtconf | grep Memory`.split[2].to_f * 1024 # reports in GB but vmstat is in MB
237
+ fraction = (total_mem - meminfo[32].to_f) / total_mem
238
+
239
+ report_pct :memory, fraction, "used\n\n#{reverse_numeric_sort_with_header(`ps -ao pmem,pid,comm`)}"
240
+ end
241
+
242
+ def darwin_top
243
+ raw = `top -l 1 | grep -i "^\\(cpu\\|physmem\\|load\\)"`.chomp
244
+ @topdata = { stamp: Time.now.to_i }
245
+ raw.each_line do |ln|
246
+ if ln.match(/Load Avg: [0-9.]+, [0-9.]+, ([0-9.])+/i)
247
+ @topdata[:load] = Regexp.last_match(1).to_f
248
+ elsif ln.match(/CPU usage: [0-9.]+% user, [0-9.]+% sys, ([0-9.]+)% idle/i)
249
+ @topdata[:cpu] = 1 - (Regexp.last_match(1).to_f / 100)
250
+ elsif (mdat = ln.match(/PhysMem: ([0-9]+)([BKMGT]) wired, ([0-9]+)([BKMGT]) active, ([0-9]+)([BKMGT]) inactive, ([0-9]+)([BKMGT]) used, ([0-9]+)([BKMGT]) free/i))
251
+ wired = mdat[1].to_i * (1024**'BKMGT'.index(mdat[2]))
252
+ active = mdat[3].to_i * (1024**'BKMGT'.index(mdat[4]))
253
+ inactive = mdat[5].to_i * (1024**'BKMGT'.index(mdat[6]))
254
+ used = mdat[7].to_i * (1024**'BKMGT'.index(mdat[8]))
255
+ free = mdat[9].to_i * (1024**'BKMGT'.index(mdat[10]))
256
+ @topdata[:memory] = (wired + active + used).to_f / (wired + active + used + inactive + free)
257
+ # This is for OSX Mavericks which
258
+ # uses a different format for top
259
+ # Example: PhysMem: 4662M used (1328M wired), 2782M unused.
260
+ elsif (mdat = ln.match(/PhysMem: ([0-9]+)([BKMGT]) used \([0-9]+[BKMGT] wired\), ([0-9]+)([BKMGT]) unused/i))
261
+ used = mdat[1].to_i * (1024**'BKMGT'.index(mdat[2]))
262
+ unused = mdat[3].to_i * (1024**'BKMGT'.index(mdat[4]))
263
+ @topdata[:memory] = used.to_f / (used + unused)
264
+ end
265
+ end
266
+ end
267
+
268
+ def darwin_cpu
269
+ darwin_top unless (Time.now.to_i - @topdata[:stamp]) < opts[:interval]
270
+ unless @topdata[:cpu]
271
+ alert 'cpu', :unknown, nil, 'unable to get CPU stats from top'
272
+ return false
273
+ end
274
+ report_pct :cpu, @topdata[:cpu], "usage\n\n#{reverse_numeric_sort_with_header(`ps -eo pcpu,pid,comm`)}"
275
+ end
276
+
277
+ def darwin_load
278
+ darwin_top unless (Time.now.to_i - @topdata[:stamp]) < opts[:interval]
279
+ unless @topdata[:load]
280
+ alert 'load', :unknown, nil, 'unable to get load ave from top'
281
+ return false
282
+ end
283
+ metric = @topdata[:load] / @cores
284
+ if metric > @limits[:load][:critical]
285
+ alert 'load', :critical, metric, "1-minute load average per core is #{metric}"
286
+ elsif metric > @limits[:load][:warning]
287
+ alert 'load', :warning, metric, "1-minute load average per core is #{metric}"
288
+ else
289
+ alert 'load', :ok, metric, "1-minute load average per core is #{metric}"
290
+ end
291
+ end
292
+
293
+ def darwin_memory
294
+ darwin_top unless (Time.now.to_i - @topdata[:stamp]) < opts[:interval]
295
+ unless @topdata[:memory]
296
+ alert 'memory', :unknown, nil, 'unable to get memory data from top'
297
+ return false
298
+ end
299
+ report_pct :memory, @topdata[:memory], "usage\n\n#{reverse_numeric_sort_with_header(`ps -eo pmem,pid,comm`)}"
300
+ end
301
+
302
+ def df
303
+ case @ostype
304
+ when 'darwin', 'freebsd', 'openbsd'
305
+ `df -P -t no#{opts[:disk_ignorefs].join(',')}`
306
+ when 'sunos'
307
+ `df -P` # Is there a good way to exlude iso9660 here?
308
+ else
309
+ if @supports_exclude_type
310
+ `df -P #{opts[:disk_ignorefs].map { |fstype| "--exclude-type=#{fstype}" }.join(' ')}`
311
+ else
312
+ `df -P`
313
+ end
314
+ end
315
+ end
316
+
317
+ def disk
318
+ df.split(/\n/).each do |r|
319
+ f = r.split(/\s+/)
320
+ next if f[0] == 'Filesystem'
321
+
322
+ # Calculate capacity
323
+ used = f[2].to_i
324
+ available = f[3].to_i
325
+ total_without_reservation = used + available
326
+
327
+ x = used.to_f / total_without_reservation
328
+
329
+ if x > @limits[:disk][:critical]
330
+ alert "disk #{f[5]}", :critical, x, "#{f[4]} used"
331
+ elsif x > @limits[:disk][:warning]
332
+ alert "disk #{f[5]}", :warning, x, "#{f[4]} used"
333
+ else
334
+ alert "disk #{f[5]}", :ok, x, "#{f[4]} used"
335
+ end
336
+ end
337
+ end
338
+
339
+ def tick
340
+ @cpu.call if @cpu_enabled
341
+ @memory.call if @memory_enabled
342
+ @disk.call if @disk_enabled
343
+ @load.call if @load_enabled
344
+ end
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'riemann/tools'
4
+
5
+ module Riemann
6
+ module Tools
7
+ class Net
8
+ include Riemann::Tools
9
+
10
+ opt :interfaces, 'Interfaces to monitor', type: :strings, default: nil
11
+ opt :ignore_interfaces, 'Interfaces to ignore', type: :strings, default: ['lo']
12
+
13
+ def initialize
14
+ @old_state = nil
15
+ @interfaces = if opts[:interfaces]
16
+ opts[:interfaces].reject(&:empty?).map(&:dup)
17
+ else
18
+ []
19
+ end
20
+ @ignore_interfaces = opts[:ignore_interfaces].reject(&:empty?).map(&:dup)
21
+ end
22
+
23
+ def state
24
+ f = File.read('/proc/net/dev')
25
+ state = {}
26
+ f.split("\n").each do |line|
27
+ next unless line =~ /\A\s*([[:alnum:]-]+?):\s*([\s\d]+)\s*/
28
+
29
+ iface = Regexp.last_match(1)
30
+
31
+ next unless @interfaces.empty? || @interfaces.any? { |pattern| iface.match?(pattern) }
32
+ next if @ignore_interfaces.any? { |pattern| iface.match?(pattern) }
33
+
34
+ ['rx bytes',
35
+ 'rx packets',
36
+ 'rx errs',
37
+ 'rx drop',
38
+ 'rx fifo',
39
+ 'rx frame',
40
+ 'rx compressed',
41
+ 'rx multicast',
42
+ 'tx bytes',
43
+ 'tx packets',
44
+ 'tx errs',
45
+ 'tx drops',
46
+ 'tx fifo',
47
+ 'tx colls',
48
+ 'tx carrier',
49
+ 'tx compressed',].map do |service|
50
+ "#{iface} #{service}"
51
+ end.zip( # rubocop:disable Style/MultilineBlockChain
52
+ Regexp.last_match(2).split(/\s+/).map(&:to_i),
53
+ ).each do |service, value|
54
+ state[service] = value
55
+ end
56
+ end
57
+
58
+ state
59
+ end
60
+
61
+ def tick
62
+ state = self.state
63
+
64
+ if @old_state
65
+ # Report services from `@old_state` that don't exist in `state` as expired
66
+ @old_state.reject { |k| state.key?(k) }.each do |service, _metric|
67
+ report(service: service.dup, state: 'expired')
68
+ end
69
+
70
+ # Report delta for services that have values in both `@old_state` and `state`
71
+ state.each do |service, metric|
72
+ next unless @old_state.key?(service)
73
+
74
+ delta = metric - @old_state[service]
75
+ svc_state = case service
76
+ when /drop$/
77
+ if delta.positive?
78
+ 'warning'
79
+ else
80
+ 'ok'
81
+ end
82
+ when /errs$/
83
+ if delta.positive?
84
+ 'warning'
85
+ else
86
+ 'ok'
87
+ end
88
+ else
89
+ 'ok'
90
+ end
91
+
92
+ report(
93
+ service: service.dup,
94
+ metric: (delta.to_f / opts[:interval]),
95
+ state: svc_state,
96
+ )
97
+ end
98
+ end
99
+
100
+ @old_state = state
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'riemann/tools'
4
+
5
+ module Riemann
6
+ module Tools
7
+ class Ntp
8
+ include Riemann::Tools
9
+
10
+ def initialize
11
+ @hostname = `hostname`.chomp
12
+ @ostype = `uname -s`.chomp.downcase
13
+ abort 'WARNING: macOS not explicitly supported. Exiting.' if @ostype == 'darwin'
14
+ end
15
+
16
+ def tick
17
+ stats = `ntpq -p -n`
18
+ stats.each_line do |stat|
19
+ m = stat.split
20
+ next if m.grep(/^===/).any? || m.grep(/^remote/).any?
21
+
22
+ @ntp_host = m[0].gsub('*', '').gsub('-', '').gsub('+', '')
23
+ send('delay', m[7])
24
+ send('offset', m[8])
25
+ send('jitter', m[9])
26
+ end
27
+ end
28
+
29
+ def send(type, metric)
30
+ report(
31
+ host: @hostname,
32
+ service: "ntp peer #{@ntp_host} #{type}",
33
+ metric: metric.to_f,
34
+ state: 'ok',
35
+ description: "ntp peer #{@ntp_host} #{type}",
36
+ tags: ['ntp'],
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Riemann
4
4
  module Tools # :nodoc:
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  end