solanum 0.2.0 → 1.0.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.
@@ -0,0 +1,44 @@
1
+ require 'solanum/source'
2
+ require 'solanum/util'
3
+
4
+ class Solanum::Source::Load < Solanum::Source
5
+ attr_reader :load_states
6
+
7
+ STAT_FILE = '/proc/loadavg'
8
+
9
+
10
+ def initialize(opts)
11
+ super(opts)
12
+ @load_states = opts['load_states'] || {}
13
+ end
14
+
15
+
16
+ def collect!
17
+ events = []
18
+
19
+ loadavg = File.read(STAT_FILE).chomp.split(' ')
20
+
21
+ load1m = loadavg[0].to_f
22
+
23
+ events << {
24
+ service: 'process load',
25
+ metric: load1m,
26
+ state: state_over(@load_states, load1m),
27
+ }
28
+
29
+ running, count = *loadavg[3].split('/')
30
+
31
+ events << {
32
+ service: 'process running',
33
+ metric: running.to_i,
34
+ }
35
+
36
+ events << {
37
+ service: 'process count',
38
+ metric: count.to_i,
39
+ }
40
+
41
+ events
42
+ end
43
+
44
+ end
@@ -0,0 +1,70 @@
1
+ require 'solanum/source'
2
+ require 'solanum/util'
3
+
4
+ class Solanum::Source::Memory < Solanum::Source
5
+ attr_reader :thresholds, :swap_thresholds
6
+
7
+
8
+ def initialize(opts)
9
+ super(opts)
10
+ @thresholds = opts['thresholds'] || {}
11
+ @swap_thresholds = opts['swap_thresholds'] || {}
12
+ end
13
+
14
+
15
+ def collect!
16
+ events = []
17
+
18
+ meminfo = Hash.new(0)
19
+ File.readlines('/proc/meminfo').each do |line|
20
+ measure, quantity = *line.chomp.split(/: +/)
21
+ value, unit = *quantity.split(' ')
22
+ meminfo[measure] = value.to_i
23
+ end
24
+
25
+ mem_total = meminfo['MemTotal'].to_f
26
+ record_usage = lambda do |type|
27
+ if meminfo[type]
28
+ usage_pct = meminfo[type]/mem_total.to_f
29
+ events << {
30
+ service: "memory #{type.downcase}",
31
+ metric: usage_pct,
32
+ }
33
+ end
34
+ end
35
+
36
+ total_used = mem_total - meminfo['MemFree']
37
+ mem_used = total_used - meminfo['Buffers'] - meminfo['Cached']
38
+ usage = mem_used/mem_total
39
+ events << {
40
+ service: 'memory usage',
41
+ metric: usage,
42
+ state: state_over(@thresholds, usage),
43
+ }
44
+
45
+ events << {
46
+ service: 'memory buffers',
47
+ metric: meminfo['Buffers']/mem_total
48
+ }
49
+
50
+ cached = meminfo['Cached'] + meminfo['SReclaimable'] - meminfo['Shmem']
51
+ events << {
52
+ service: 'memory cached',
53
+ metric: cached/mem_total
54
+ }
55
+
56
+ if meminfo['SwapTotal'] && meminfo['SwapTotal'] > 0
57
+ swap_total = meminfo['SwapTotal']
58
+ swap_free = meminfo['SwapFree']
59
+ usage = 1.0 - swap_free/swap_total.to_f
60
+ events << {
61
+ service: 'swap usage',
62
+ metric: usage,
63
+ state: state_over(@swap_thresholds, usage),
64
+ }
65
+ end
66
+
67
+ events
68
+ end
69
+
70
+ end
@@ -0,0 +1,65 @@
1
+ require 'solanum/source'
2
+
3
+ # bytes - The total number of bytes of data transmitted or received by the interface.
4
+ # packets - The total number of packets of data transmitted or received by the interface.
5
+ # errs - The total number of transmit or receive errors detected by the device driver.
6
+ # drop - The total number of packets dropped by the device driver.
7
+ # fifo - The number of FIFO buffer errors.
8
+ # frame - The number of packet framing errors.
9
+ # colls - The number of collisions detected on the interface.
10
+ # compressed - The number of compressed packets transmitted or received by the device driver. (This appears to be unused in the 2.2.15 kernel.)
11
+ # carrier - The number of carrier losses detected by the device driver.
12
+ # multicast - The number of multicast frames transmitted or received by the device driver.
13
+ class Solanum::Source::Network < Solanum::Source
14
+ attr_reader :interfaces, :detailed
15
+
16
+ STAT_FILE = '/proc/net/dev'
17
+
18
+ FIELDS = %w{
19
+ rx_bytes rx_packets rx_errs rx_drop rx_fifo rx_frame rx_compressed rx_multicast
20
+ tx_bytes tx_packets tx_errs tx_drop tx_fifo tx_colls tx_carrier tx_compressed
21
+ }
22
+
23
+ SIMPLE_FIELDS = %w{rx_bytes rx_packets tx_bytes tx_packets}
24
+
25
+
26
+ def initialize(opts)
27
+ super(opts)
28
+ @interfaces = opts['interfaces'] || []
29
+ @detailed = opts['detailed'] || false
30
+ @last = {}
31
+ end
32
+
33
+
34
+ def parse_stats(line)
35
+ columns = line.strip.split(/\s+/)
36
+ iface = columns.shift.chomp(':')
37
+ return iface, Hash[FIELDS.zip(columns.map(&:to_i))]
38
+ end
39
+
40
+
41
+ def collect!
42
+ events = []
43
+
44
+ File.readlines(STAT_FILE).drop(2).each do |line|
45
+ iface, stats = parse_stats(line)
46
+
47
+ if @interfaces.empty? || @interfaces.include?(iface)
48
+ if @last[iface]
49
+ FIELDS.each do |field|
50
+ next unless @detailed || SIMPLE_FIELDS.include?(field)
51
+ diff = stats[field] - @last[iface][field]
52
+ events << {
53
+ service: "net #{iface} #{field.gsub('_', ' ')}",
54
+ metric: diff,
55
+ }
56
+ end
57
+ end
58
+ @last[iface] = stats
59
+ end
60
+ end
61
+
62
+ events
63
+ end
64
+
65
+ end
@@ -0,0 +1,28 @@
1
+ require 'solanum/source'
2
+ require 'solanum/util'
3
+
4
+ class Solanum::Source::Uptime < Solanum::Source
5
+
6
+ STAT_FILE = '/proc/uptime'
7
+
8
+
9
+ def initialize(opts)
10
+ super(opts)
11
+ end
12
+
13
+
14
+ def collect!
15
+ events = []
16
+
17
+ uptime = File.read(STAT_FILE).split(' ').first.to_f
18
+
19
+ events << {
20
+ service: 'uptime',
21
+ metric: uptime,
22
+ description: "Up for #{duration_str(uptime)}",
23
+ }
24
+
25
+ events
26
+ end
27
+
28
+ end
@@ -0,0 +1,40 @@
1
+ # Return a human-friendly duration string for the given duration in seconds.
2
+ def duration_str(duration)
3
+ days = (duration/86400).to_i
4
+ hours = ((duration % 86400)/3600).to_i
5
+ minutes = ((duration % 3600)/60).to_i
6
+ seconds = (duration % 60).to_i
7
+ hms = "%02d:%02d:%02d" % [hours, minutes, seconds]
8
+
9
+ if 0 < days
10
+ "#{days} days, #{hms}"
11
+ else
12
+ hms
13
+ end
14
+ end
15
+
16
+
17
+ # Calculate the state of a metric by comparing it to the given thresholds. The
18
+ # metric is compared to each threshold in turn, largest to smallest. The first
19
+ # threshold the metric is larger than is returned, or the 'min_sate' is
20
+ # returned.
21
+ def state_over(thresholds, metric, min_state='ok')
22
+ thresholds.sort_by {|e| -e[1] }.each do |threshold_entry|
23
+ key, threshold = *threshold_entry
24
+ return key if threshold <= metric
25
+ end
26
+ return min_state
27
+ end
28
+
29
+
30
+ # Calculate the state of a metric by comparing it to the given thresholds. The
31
+ # metric is compared to each threshold in turn, smallest to largest. The first
32
+ # threshold the metric is smaller than is returned, or the 'max_state' is
33
+ # returned.
34
+ def state_under(thresholds, metric, max_state='ok')
35
+ thresholds.sort_by {|e| e[1] }.each do |threshold_entry|
36
+ key, threshold = *threshold_entry
37
+ return key if threshold > metric
38
+ end
39
+ return max_state
40
+ end
metadata CHANGED
@@ -1,64 +1,79 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solanum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Greg Look
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-07-28 00:00:00.000000000 Z
12
+ date: 2017-12-23 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: riemann-client
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
- version: 0.2.2
21
+ version: 0.2.6
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
- version: 0.2.2
29
+ version: 0.2.6
27
30
  description:
28
- email: greg@greg-look.net
31
+ email: greg@greglook.net
29
32
  executables:
30
33
  - solanum
31
34
  extensions: []
32
35
  extra_rdoc_files: []
33
36
  files:
34
- - lib/solanum/config.rb
35
- - lib/solanum/matcher.rb
36
37
  - lib/solanum/source.rb
38
+ - lib/solanum/schedule.rb
39
+ - lib/solanum/config.rb
40
+ - lib/solanum/source/cpu.rb
41
+ - lib/solanum/source/memory.rb
42
+ - lib/solanum/source/diskstats.rb
43
+ - lib/solanum/source/uptime.rb
44
+ - lib/solanum/source/network.rb
45
+ - lib/solanum/source/load.rb
46
+ - lib/solanum/source/certificate.rb
47
+ - lib/solanum/output/print.rb
48
+ - lib/solanum/output/riemann.rb
49
+ - lib/solanum/util.rb
37
50
  - lib/solanum.rb
38
51
  - bin/solanum
39
52
  - README.md
40
53
  homepage: https://github.com/greglook/solanum
41
54
  licenses:
42
55
  - Public Domain
43
- metadata: {}
44
56
  post_install_message:
45
57
  rdoc_options: []
46
58
  require_paths:
47
59
  - lib
48
60
  required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
49
62
  requirements:
50
- - - '>='
63
+ - - ! '>='
51
64
  - !ruby/object:Gem::Version
52
- version: 1.9.1
65
+ version: 1.9.3
53
66
  required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
54
68
  requirements:
55
- - - '>='
69
+ - - ! '>='
56
70
  - !ruby/object:Gem::Version
57
71
  version: '0'
58
72
  requirements: []
59
73
  rubyforge_project:
60
- rubygems_version: 2.0.14
74
+ rubygems_version: 1.8.23
61
75
  signing_key:
62
- specification_version: 4
63
- summary: DSL for custom monitoring configuration
76
+ specification_version: 3
77
+ summary: Extensible monitoring daemon
64
78
  test_files: []
79
+ has_rdoc:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: e9ebaaa5f2e140d9a0b2d821bc685a363f103ea1
4
- data.tar.gz: 4fc6fbb8e60b919d8a9d1d8c8d36d557a80269ca
5
- SHA512:
6
- metadata.gz: 30e99d6aa3fba2cf78f4ba69bde44e799212ca457a7de325ee40585e111c237188b3b1d0770c642a86641da5c253eab09a64310ae4a39fc2f7be6cee19a7d4f6
7
- data.tar.gz: 254e37a291a4e3925e3a0e38cb7fde12f4a177508ba0354ed6ba035d2844fd086411ed040eedb63d421f51f2841f4d9277d9b8b32a26f232497f547a2a7e0160
@@ -1,70 +0,0 @@
1
- require 'json'
2
-
3
- # A matcher takes in an input string and returns a hash of measurement names to
4
- # numeric values.
5
- #
6
- # Author:: Greg Look
7
- class Solanum::Matcher
8
- attr_reader :fn
9
-
10
- # Creates a new Matcher which will run the given function on input.
11
- def initialize(fn)
12
- raise "function must be provided" if fn.nil?
13
- @fn = fn
14
- end
15
-
16
- # Attempts to match the given input, returning a hash of metrics.
17
- def call(input)
18
- {}
19
- end
20
-
21
-
22
- ### MATCHER TYPES ###
23
-
24
- public
25
-
26
- # LinePattern matchers define a regular expression which is tested against
27
- # each line of input. The given function is called for **each** matched line,
28
- # and the resulting measurements are merged together.
29
- class LinePattern < Solanum::Matcher
30
- def initialize(fn, pattern)
31
- super fn
32
- raise "pattern must be provided" if pattern.nil?
33
- @pattern = pattern
34
- end
35
-
36
- def call(input)
37
- raise "No input provided!" if input.nil?
38
- lines = input.split("\n")
39
- metrics = {}
40
-
41
- lines.each do |line|
42
- begin
43
- if @pattern === line
44
- measurements = @fn.call($~)
45
- metrics.merge!(measurements) if measurements
46
- end
47
- rescue => e
48
- STDERR.puts("Error calculating metrics from line match: #{e.inspect}")
49
- end
50
- end
51
-
52
- metrics
53
- end
54
- end
55
-
56
-
57
- # JsonReader matches a JSON-formatted input string and passes the parsed
58
- # object to the block body.
59
- class JSONReader < Solanum::Matcher
60
- def call(input)
61
- begin
62
- json = JSON.parse(input)
63
- @fn.call(json)
64
- rescue => e
65
- STDERR.puts("Error matching JSON input: #{e.inspect}")
66
- {}
67
- end
68
- end
69
- end
70
- end