remon 0.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 (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: []