remon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/FEATURES.md +39 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +41 -0
  8. data/Rakefile +27 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/dev_exe/remon +4 -0
  12. data/exe/remon +101 -0
  13. data/lib/remon.rb +11 -0
  14. data/lib/remon/check.rb +145 -0
  15. data/lib/remon/check_dsl.rb +92 -0
  16. data/lib/remon/check_runner.rb +53 -0
  17. data/lib/remon/checks/consul.rb +41 -0
  18. data/lib/remon/checks/disk.rb +36 -0
  19. data/lib/remon/checks/http.rb +53 -0
  20. data/lib/remon/checks/oom.rb +26 -0
  21. data/lib/remon/checks/redis.rb +23 -0
  22. data/lib/remon/checks/salt.rb +27 -0
  23. data/lib/remon/checks/system.rb +96 -0
  24. data/lib/remon/checks/yum.rb +30 -0
  25. data/lib/remon/config.rb +101 -0
  26. data/lib/remon/custom_logger.rb +6 -0
  27. data/lib/remon/deduped_queue.rb +38 -0
  28. data/lib/remon/error.rb +4 -0
  29. data/lib/remon/event_processor.rb +33 -0
  30. data/lib/remon/ext/num_ext.rb +23 -0
  31. data/lib/remon/helper.rb +41 -0
  32. data/lib/remon/logger.rb +17 -0
  33. data/lib/remon/metrics/consul.rb +32 -0
  34. data/lib/remon/metrics/disk.rb +24 -0
  35. data/lib/remon/metrics/http.rb +40 -0
  36. data/lib/remon/metrics/oom.rb +32 -0
  37. data/lib/remon/metrics/salt.rb +18 -0
  38. data/lib/remon/metrics/system.rb +63 -0
  39. data/lib/remon/metrics/yum.rb +20 -0
  40. data/lib/remon/proc_check.rb +26 -0
  41. data/lib/remon/scheduler.rb +106 -0
  42. data/lib/remon/scripts/salt-status +24 -0
  43. data/lib/remon/scripts/yum-status +12 -0
  44. data/lib/remon/sysinfo.rb +69 -0
  45. data/lib/remon/version.rb +3 -0
  46. data/remon.gemspec +26 -0
  47. data/test_config.rb +44 -0
  48. metadata +146 -0
@@ -0,0 +1,63 @@
1
+ require 'socket'
2
+ require_relative '../sysinfo'
3
+
4
+ module Remon
5
+ module Metrics
6
+ class System
7
+
8
+ CpuStat = Struct.new(:used, :idle, :iowait)
9
+
10
+ def memory
11
+ m = {}
12
+ IO.foreach("/proc/meminfo").first(6).each do |line|
13
+ split = line.split(":")
14
+ key = split[0].to_sym
15
+ value = split[1].split[0]
16
+ m[key] = value
17
+ end
18
+ free = m[:MemAvailable].to_i || (m[:MemFree].to_i + m[:Buffers].to_i + m[:Cached].to_i)
19
+ total = m[:MemTotal].to_i
20
+ used = (1 - (free.to_f / total)).round(4)
21
+ end
22
+
23
+ def loadavg
24
+ File.open("/proc/loadavg") { |f| f.gets(" ").to_f }
25
+ end
26
+
27
+ def loadavg_normalized
28
+ (loadavg / Sysinfo.normalized_cores).round 2
29
+ end
30
+
31
+ def uptime
32
+ File.open('/proc/uptime') { |f| f.gets(" ").to_f }
33
+ end
34
+
35
+ def cpu_stat
36
+ stat = File.open('/proc/stat', 'r') { |f| f.readline }
37
+ @stat_regex ||= /^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/
38
+ mdata = stat.match(@stat_regex)
39
+ if mdata
40
+ user, nice, system = [mdata[1], mdata[2], mdata[3]].map { |i| i.to_i }
41
+ cpu = CpuStat.new
42
+ cpu.used = user + nice + system
43
+ cpu.idle = mdata[4].to_i
44
+ cpu.iowait = mdata[5].to_i
45
+ return cpu
46
+ else
47
+ return nil
48
+ end
49
+ end
50
+
51
+ def cpu_usage(old_cpu, new_cpu)
52
+ used = new_cpu.used - old_cpu.used
53
+ idle = new_cpu.idle - old_cpu.idle
54
+ iowait = new_cpu.iowait - old_cpu.iowait
55
+ total = used + idle + iowait
56
+ used_frac = (used.to_f / total).round 4
57
+ iowait_frac = (iowait.to_f / total).round 4
58
+ [used_frac, iowait_frac]
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ require 'remon/helper'
2
+
3
+ module Remon
4
+ module Metrics
5
+ class Yum
6
+ include Helper
7
+
8
+ def initialize(timeout: 240)
9
+ @timeout = timeout
10
+ end
11
+
12
+ def updates_available
13
+ script = File.expand_path "#{__dir__}/../scripts/yum-status"
14
+ out = cmd "timeout -k 60s #{@timeout}s python #{script} 2>/dev/null", return_output: true, shell: true
15
+ count = out.chomp.to_i
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'check'
2
+ module Remon
3
+ class ProcCheck < Check
4
+
5
+ attr_reader :name
6
+ def initialize(name, pr)
7
+ raise "no proc passed" if not pr.is_a?(Proc)
8
+ @name = name
9
+ @pr = pr
10
+ @mutex = Mutex.new
11
+ end
12
+
13
+ def run
14
+ instance_eval &@pr
15
+ end
16
+
17
+ def check_name
18
+ @name
19
+ end
20
+
21
+ def to_s
22
+ "<proc:#{@name}>"
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,106 @@
1
+ require_relative 'logger'
2
+ require_relative 'error'
3
+
4
+ module Remon
5
+ class Scheduler
6
+
7
+ include Logger
8
+ attr_reader :queue
9
+
10
+ def initialize(schedule, queue:, scheduler_offset: 0)
11
+ @schedule = schedule
12
+ @queue = queue
13
+ @scheduler_pipeline = {}
14
+ @scheduler_offset = scheduler_offset
15
+ validate_schedule
16
+ end
17
+
18
+ def run(n = :inf, show_progress: true)
19
+ logger.debug "starting scheduler"
20
+ ticker(n, show_progress) do |t|
21
+ schedule_tasks (t + @scheduler_offset)
22
+ end
23
+ end
24
+
25
+ def schedule_tasks(time)
26
+ task_groups = keys.select { |i| time % i[:interval] == 0}
27
+ task_groups.each do |tg|
28
+ pipeline_task_group tg, time
29
+ end
30
+ enqueue_tasks(time)
31
+ end
32
+
33
+ def ticker(n, show_progress = false)
34
+ t = Time.now.to_i
35
+ case n
36
+ when Integer
37
+ n.times do |i|
38
+ puts i if show_progress
39
+ yield t
40
+ t = t + 1
41
+ sleep_till t
42
+ end
43
+ else
44
+ loop do
45
+ yield t
46
+ t = t + 1
47
+ sleep_till t
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def keys
55
+ @keys ||= @schedule.keys
56
+ end
57
+
58
+ def sleep_till(t)
59
+ diff = (t - Time.now.to_f)
60
+ sleep diff if diff > 0
61
+ end
62
+
63
+ def pipeline_task_group(task_group, time)
64
+ randomize = task_group[:randomize]
65
+ offset = task_group[:offset]
66
+
67
+ if randomize && (offset > 0)
68
+ offset = rand(0..offset)
69
+ end
70
+
71
+ tasks = @schedule[task_group]
72
+ t = time + offset
73
+ @scheduler_pipeline[t] ||= Set.new
74
+ tasks.each { |task| @scheduler_pipeline[t] << task }
75
+ end
76
+
77
+
78
+ def enqueue_tasks(time)
79
+ tasks = @scheduler_pipeline[time]
80
+ return if not tasks
81
+ tasks.each do |t|
82
+ logger.debug "scheduling #{t} for time #{time}" if logger.debug?
83
+ @queue << t
84
+ end
85
+ @scheduler_pipeline.delete(time)
86
+ end
87
+
88
+ def validate_schedule
89
+ if not @schedule.is_a? Hash
90
+ raise Error, "invalid schedule: not a hash"
91
+ end
92
+
93
+ @schedule.each do |task_group, tasks|
94
+ raise Error, "invalid task_group: #{task_group}" if not valid_task_group? task_group
95
+ end
96
+ end
97
+
98
+ def valid_task_group?(t)
99
+ required_keys = [:interval, :randomize, :offset]
100
+ return false if not t.is_a? Hash
101
+ return false if not t.keys.all? { |i| required_keys.include? i }
102
+ return true
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require 'json'
3
+
4
+ def salt_state(output)
5
+ out = JSON.parse(output)
6
+ states = out["local"].values
7
+ drifted_states = states.reject { |i| i["result"] }
8
+ num_ok = states.count - drifted_states.count
9
+ total = states.count
10
+ state = num_ok == total ? "ok" : "warning"
11
+ "#{state}:#{num_ok}:#{total}"
12
+ end
13
+
14
+ timeout = ARGV.shift || 240
15
+ begin
16
+ out = `timeout -k 60s #{timeout}s salt-call state.highstate test=True --out=json --state-output=changes 2>/dev/null`
17
+ if $?.exitstatus == 0
18
+ puts salt_state(out)
19
+ else
20
+ puts "error:unknown:unknown"
21
+ end
22
+ rescue
23
+ puts "exception:unknown:unknown"
24
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/python
2
+ import yum
3
+ import json
4
+
5
+ base = yum.YumBase()
6
+ package_list = base.doPackageLists(pkgnarrow='updates', patterns='', ignore_case=True)
7
+
8
+ if package_list.updates:
9
+ count = len(package_list.updates)
10
+ else:
11
+ count = 0
12
+ print count
@@ -0,0 +1,69 @@
1
+ require 'socket'
2
+ require_relative 'helper'
3
+
4
+ module Remon
5
+ class Sysinfo
6
+ extend Helper
7
+
8
+ def self.init
9
+ puts "loading sysinfo"
10
+ nproc
11
+ cores
12
+ normalized_cores
13
+ instance_type
14
+ ips
15
+ end
16
+
17
+ def self.instance_type
18
+ @instance_type ||= begin
19
+ exitstatus, instance_type = safe_cmd 'curl -s -m2 169.254.169.254/latest/meta-data/instance-type', return_output: true
20
+ exitstatus == 0 ? instance_type : "unknown"
21
+ end
22
+ end
23
+
24
+ def self.nproc
25
+ @nproc ||= `nproc`.chomp.strip.to_i
26
+ end
27
+
28
+ def self.cores
29
+ @cores ||= cores_linux
30
+ end
31
+
32
+ def self.normalized_cores
33
+ @normalized_cores ||= begin
34
+ cores = self.cores
35
+ nproc = self.nproc
36
+ (cores == nproc) ? cores : (cores + (nproc - cores)* 30.0/100)
37
+ end
38
+ end
39
+
40
+ def self.ips
41
+ @ips ||= begin
42
+ addr_infos = Socket.ip_address_list.select do |i|
43
+ i.ip_address =~ /\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/
44
+ end
45
+ addr_infos.reject! { |i| i.ip_address == "127.0.0.1" }
46
+ ips = addr_infos.map { |i| i.ip_address }
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def self.cores_linux
53
+ i = 0;
54
+ File.read("/proc/cpuinfo").split(/\n\n/).inject({}) do |cores, p|
55
+ physical_id = p[/physical id\s+:\s+(\d+)/, 1]
56
+ core_id = p[/core id\s+:\s+(\d+)/, 1]
57
+ if physical_id and core_id
58
+ cores["#{physical_id}:#{core_id}"] = true
59
+ elsif physical_id
60
+ cores["#{physical_id}:"] = true
61
+ else
62
+ cores[i += 1] = true;
63
+ end
64
+ cores
65
+ end.size
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Remon
2
+ VERSION = "0.1.0"
3
+ end
data/remon.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'remon/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "remon"
8
+ spec.version = Remon::VERSION
9
+ spec.authors = ["Neeraj"]
10
+
11
+ spec.summary = %q{Health check library for system/services like postgres/elasticsearch/redis etc}
12
+ spec.description = %q{Health check library for system/services like postgres/elasticsearch}
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.13.a"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency "simplecov", "~> 0.12.0"
26
+ end
data/test_config.rb ADDED
@@ -0,0 +1,44 @@
1
+ puts Process.pid
2
+ require 'pp'
3
+
4
+ host "test-#{Socket.gethostname}"
5
+
6
+ workers 1
7
+ process_event do |event|
8
+ printf "%-10s %-10s %-10s %-8s %-10s\n", Time.now, event[:host], event[:state], event[:service], event[:metric]
9
+ end
10
+
11
+ mysys = check(:system).new( opts: {cpu_warning: 45})
12
+ google_check = check(:http).new("http://google.com")
13
+
14
+ filename = "test_config.rb"
15
+
16
+ file_check = proc_check "test" do
17
+ sleep 3
18
+ metric = File.exist?(filename) ? 0 : 1
19
+ description = metric == 0 ? "file exists" : "file doesn't exist"
20
+ state = state(metric, warning: 1, critical: 2)
21
+ event({
22
+ service: name,
23
+ description: description,
24
+ metric: metric,
25
+ state: state
26
+ })
27
+ end
28
+
29
+ oom_check = check(:oom).new(log_file: "spec/test_data/messages")
30
+
31
+ every 1.seconds, offset: 0.seconds, randomize: true do
32
+ run_check mysys
33
+ run_check :disk
34
+ run_check oom_check
35
+ end
36
+
37
+ every 5.seconds, offset: 2.seconds do
38
+ run_check google_check
39
+ end
40
+
41
+ every 60.seconds do
42
+ # run_check oom_check
43
+ # run_check google_check
44
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Neeraj
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.13.a
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.13.a
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.12.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.12.0
69
+ description: Health check library for system/services like postgres/elasticsearch
70
+ email:
71
+ executables:
72
+ - remon
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - FEATURES.md
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - dev_exe/remon
86
+ - exe/remon
87
+ - lib/remon.rb
88
+ - lib/remon/check.rb
89
+ - lib/remon/check_dsl.rb
90
+ - lib/remon/check_runner.rb
91
+ - lib/remon/checks/consul.rb
92
+ - lib/remon/checks/disk.rb
93
+ - lib/remon/checks/http.rb
94
+ - lib/remon/checks/oom.rb
95
+ - lib/remon/checks/redis.rb
96
+ - lib/remon/checks/salt.rb
97
+ - lib/remon/checks/system.rb
98
+ - lib/remon/checks/yum.rb
99
+ - lib/remon/config.rb
100
+ - lib/remon/custom_logger.rb
101
+ - lib/remon/deduped_queue.rb
102
+ - lib/remon/error.rb
103
+ - lib/remon/event_processor.rb
104
+ - lib/remon/ext/num_ext.rb
105
+ - lib/remon/helper.rb
106
+ - lib/remon/logger.rb
107
+ - lib/remon/metrics/consul.rb
108
+ - lib/remon/metrics/disk.rb
109
+ - lib/remon/metrics/http.rb
110
+ - lib/remon/metrics/oom.rb
111
+ - lib/remon/metrics/salt.rb
112
+ - lib/remon/metrics/system.rb
113
+ - lib/remon/metrics/yum.rb
114
+ - lib/remon/proc_check.rb
115
+ - lib/remon/scheduler.rb
116
+ - lib/remon/scripts/salt-status
117
+ - lib/remon/scripts/yum-status
118
+ - lib/remon/sysinfo.rb
119
+ - lib/remon/version.rb
120
+ - remon.gemspec
121
+ - test_config.rb
122
+ homepage:
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubygems_version: 3.0.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Health check library for system/services like postgres/elasticsearch/redis
145
+ etc
146
+ test_files: []