eznemo 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c3efce5df3e500b3170a41590e844315147277f9
4
- data.tar.gz: fddcc93d34f4c32d015b257f33447bbbb555ad67
3
+ metadata.gz: 4a63d6de019cde7d7b7f154e16534524759075ad
4
+ data.tar.gz: 930d98134aeb498f387fb8bcd69866948cb82dc0
5
5
  SHA512:
6
- metadata.gz: 211605c26eae2da41969b4be18e2d3d4d02f57604ff9ae06af6799b02eb3fc061b682d52bf8fad7638700acc2a5e3e580a9aaa63e0141ee774196aa08832f5d4
7
- data.tar.gz: eaf38d99f061d70917681a41e1f02cb327d2499cdaaaf1eff50d8e6246d349a67d250ecbc3133babd53651d70904c83b6ada2429f35c93981e7c6a3a38afa16f
6
+ metadata.gz: e32c9ed5244114be6ea97713322558566fd1d4d2982b71046223700224d4092e4ede0f1b27d32529fc021f94eebed7be1ac02aca6537867a463763b15b721db9
7
+ data.tar.gz: 47945030a40c8e762568ec593e819309b42a93ee5347ec9ce25fd0f75604b9d45889bae290eac96ce3667b48d870dbcd3dc70a598fcc5ea643a77fa81bad2d5f
data/README.md CHANGED
@@ -40,7 +40,8 @@ Config file.
40
40
  :probe:
41
41
  :name: Probe01
42
42
  :datastore:
43
- :type: :mysql
43
+ :type: :mysql # currently the only option
44
+ :queue_size: 20
44
45
  :options:
45
46
  :host: 127.0.0.1
46
47
  :username: user
@@ -70,7 +71,6 @@ Config file.
70
71
  interval: 60, # frequecy this check is run in seconds
71
72
  type: 'ping', # or other monitor plugin name
72
73
  state: true, # true means active
73
- tags: '["tag1", "tag2"]',
74
74
  options: '-S 192.168.0.11'
75
75
  }
76
76
  ```
@@ -88,8 +88,20 @@ Config file.
88
88
  }
89
89
  ```
90
90
 
91
+ ### Tags
92
+
93
+ ```ruby
94
+ {
95
+ check_id: 123, # from checks
96
+ text: 'prod' # tag text
97
+ }
98
+ ```
99
+
100
+
91
101
  ### MySQL
92
102
 
103
+ Example using TokuDB.
104
+
93
105
  ```sql
94
106
  CREATE TABLE `checks` (
95
107
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
@@ -98,10 +110,10 @@ CREATE TABLE `checks` (
98
110
  `interval` int(11) NOT NULL,
99
111
  `type` varchar(255) NOT NULL DEFAULT '',
100
112
  `state` tinyint(1) NOT NULL,
101
- `tags` text,
102
113
  `options` text,
103
- PRIMARY KEY (`id`)
104
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
114
+ PRIMARY KEY (`id`),
115
+ CLUSTERING KEY `state` (`state`)
116
+ ) ENGINE=TokuDB DEFAULT CHARSET=utf8;
105
117
 
106
118
  CREATE TABLE `results` (
107
119
  `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -112,9 +124,18 @@ CREATE TABLE `results` (
112
124
  `response_ms` float NOT NULL DEFAULT '0',
113
125
  `status_desc` varchar(255) DEFAULT NULL,
114
126
  PRIMARY KEY (`id`),
115
- KEY `check_id` (`check_id`),
127
+ CLUSTERING KEY `check_id` (`check_id`),
116
128
  KEY `probe` (`probe`),
117
129
  KEY `timestamp` (`timestamp`),
118
130
  KEY `status` (`status`)
119
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
131
+ ) ENGINE=TokuDB DEFAULT CHARSET=utf8;
132
+
133
+ CREATE TABLE `tags` (
134
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
135
+ `check_id` int(11) NOT NULL,
136
+ `text` varchar(63) NOT NULL DEFAULT '',
137
+ PRIMARY KEY (`id`),
138
+ KEY `check_id` (`check_id`),
139
+ CLUSTERING KEY `text` (`text`)
140
+ ) ENGINE=TokuDB DEFAULT CHARSET=utf8;
120
141
  ```
data/eznemo.gemspec CHANGED
@@ -20,4 +20,5 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.add_runtime_dependency 'kajiki', '~> 1.1'
22
22
  s.add_runtime_dependency 'eventmachine', '~> 1.0'
23
+ s.add_runtime_dependency 'sequel', '~> 4.0'
23
24
  end
@@ -5,6 +5,19 @@ module EzNemo
5
5
  @datastore ||= DataStore.new
6
6
  end
7
7
 
8
+ # Sequel models
9
+ class Check < Sequel::Model
10
+ one_to_many :tags
11
+ end
12
+
13
+ class Tag < Sequel::Model
14
+ many_to_one :check
15
+ end
16
+
17
+ class Result < Sequel::Model
18
+ many_to_one :check
19
+ end
20
+
8
21
  # Storage for checks and results
9
22
  class DataStore
10
23
 
@@ -37,7 +37,7 @@ module EzNemo
37
37
  end
38
38
 
39
39
  # Add a check using this plugin
40
- # @param check [Hash]
40
+ # @param check [EzNemo::Check]
41
41
  def add_check(check)
42
42
  min = config[:min_interval]
43
43
  check[:interval] = min if check[:interval] < min
@@ -47,67 +47,84 @@ module EzNemo
47
47
  end
48
48
 
49
49
  def linux_ping(check)
50
- result = {
51
- timestamp: Time.now,
52
- check_id: check[:id]
53
- }
54
- path = config[:path]
55
- timeout = config[:timeout]
56
- options = "#{config[:cmd_opts]} #{check[:options]}"
57
- hostname = check[:hostname]
58
- cmd = "#{path} -c 1 -nqW #{timeout} #{options} #{hostname}"
59
- EM.system(cmd) do |output, status|
50
+ result = create_result_for_check(check)
51
+ EM.system(build_cmd(check)) do |output, status|
60
52
  case status.exitstatus
61
53
  when 0
62
54
  expr = /=\s*([0-9\.]+)/
63
55
  expr =~ output
64
- result[:status] = 1
65
- result[:response_ms] = $1.to_f
66
- result[:status_desc] = 'OK'
56
+ set_ok_result(result, $1.to_f)
67
57
  when 1
68
- result[:status] = 0
69
- result[:response_ms] = 0
70
- result[:status_desc] = 'NG'
58
+ set_ng_result(result)
71
59
  else
72
- output = 'see log' if output.nil? || output.size == 0
73
- result[:status] = 0
74
- result[:response_ms] = 0
75
- result[:status_desc] = "ERROR: #{output}".chomp
60
+ set_error_result(result, output)
76
61
  end
77
62
  monitor.report(result)
78
63
  end
79
64
  end
80
65
 
81
66
  def bsd_ping(check)
82
- result = {
83
- timestamp: Time.now,
84
- check_id: check[:id]
85
- }
86
- timeout = config[:timeout] * 1000
87
- options = "#{config[:cmd_opts]} #{check[:option]}"
88
- hostname = check[:hostname]
89
- cmd = "#{path} -c 1 -nqW #{timeout} #{options} #{hostname}"
90
- EM.system(cmd) do |output, status|
67
+ result = create_result_for_check(check)
68
+ args = {timeout: config[:timeout] * 1000}
69
+ EM.system(build_cmd(check, args)) do |output, status|
91
70
  case status.exitstatus
92
71
  when 0
93
72
  expr = /=\s*([0-9\.]+)/
94
73
  expr =~ output
95
- result[:status] = 1
96
- result[:response_ms] = $1.to_f
97
- result[:status_desc] = 'OK'
74
+ set_ok_result(result, $1.to_f)
98
75
  when 2
99
- result[:status] = 0
100
- result[:response_ms] = 0
101
- result[:status_desc] = 'NG'
76
+ set_ng_result(result)
102
77
  else
103
- result[:status] = 0
104
- result[:response_ms] = 0
105
- result[:status_desc] = "ERROR: #{output}".chomp
78
+ set_error_result(result, output)
106
79
  end
107
80
  monitor.report(result)
108
81
  end
109
82
  end
110
83
 
84
+ private
85
+
86
+ # @param check [EzNemo::Check]
87
+ # @return [EzNemo::Result]
88
+ def create_result_for_check(check)
89
+ Result::new do |r|
90
+ r.timestamp = Time.now
91
+ r.check = check
92
+ r.probe = EzNemo.config[:probe][:name]
93
+ end
94
+ end
95
+
96
+ # @param check [EzNemo::Check]
97
+ # @param args [Hash] overriding arguments
98
+ def build_cmd(check, args = {})
99
+ h = {
100
+ path: config[:path],
101
+ timeout: config[:timeout],
102
+ options: "#{config[:cmd_opts]} #{check[:options]}",
103
+ hostname: check[:hostname]
104
+ }
105
+ h.merge!(args)
106
+ "#{h[:path]} -c 1 -nqW #{h[:timeout]} #{h[:options]} #{h[:hostname]}"
107
+ end
108
+
109
+ def set_ok_result(result, ms)
110
+ result.status = true
111
+ result.response_ms = ms
112
+ result.status_desc = 'OK'
113
+ end
114
+
115
+ def set_ng_result(result)
116
+ result.status = false
117
+ result.response_ms = 0
118
+ result.status_desc = 'NG'
119
+ end
120
+
121
+ def set_err_result(result, msg)
122
+ msg = 'see log' if msg.nil? || msg.size == 0
123
+ result.status = false
124
+ result.response_ms = 0
125
+ result.status_desc = "ERROR: #{msg}".chomp
126
+ end
127
+
111
128
  end
112
129
 
113
130
  monitor.register(Ping.new)
@@ -1,5 +1,3 @@
1
- require 'json'
2
-
3
1
  module EzNemo
4
2
 
5
3
  # The shared Monitor instance
@@ -25,13 +23,8 @@ module EzNemo
25
23
  # Starts check loops in the reactor
26
24
  # @param checks [Array<Hash, ...>]
27
25
  def start_checks(checks)
28
- cfg_tags = EzNemo.config[:checks][:tags]
29
26
  i = 0
30
27
  checks.each do |c|
31
- if cfg_tags
32
- c[:tags] ? tags = JSON.parse(c[:tags]) : tags = []
33
- next if (cfg_tags & tags).empty?
34
- end
35
28
  p = @plugins[c[:type].to_sym]
36
29
  p.add_check(c)
37
30
  i += 1
@@ -40,7 +33,7 @@ module EzNemo
40
33
  end
41
34
 
42
35
  # Report result; usually called by the plugin
43
- # @param result [Hash] :check_id, :timestamp, :status, :response_ms, :status_desc
36
+ # @param result [EzNemo::Result]
44
37
  def report(result)
45
38
  EzNemo.datastore.store_result(result)
46
39
  end
data/lib/eznemo/mysql.rb CHANGED
@@ -1,27 +1,27 @@
1
1
  require 'mysql2/em'
2
+ require 'sequel'
3
+ require 'thread'
2
4
 
3
5
 
4
6
  module EzNemo
5
7
 
8
+ # Sequel connection setup
9
+ Sequel::Model.db = Sequel.connect({adapter: 'mysql2'}.merge(config[:datastore][:options]))
10
+
6
11
  # Defines DataStorage class for MySQL
7
12
  module StorageAdapter
8
13
 
9
14
  # Number of records it queues up before writing
10
- QUEUE_SIZE = 20
15
+ DEFAULT_QUEUE_SIZE = 20
11
16
 
12
17
  def initialize
13
18
  @results = []
14
- @probe = EzNemo.config[:probe][:name]
19
+ @queue_size = EzNemo.config[:datastore][:queue_size]
20
+ @queue_size ||= DEFAULT_QUEUE_SIZE
15
21
  @opts = EzNemo.config[:datastore][:options]
16
22
  @opts[:flags] = Mysql2::Client::MULTI_STATEMENTS
17
23
  end
18
24
 
19
- # Creates and returns new instance of {Mysql2::Client}
20
- # @return [Mysql2::Client]
21
- def database
22
- Mysql2::Client.new(@opts)
23
- end
24
-
25
25
  # Creates and returns new instance of {Mysql2::EM::Client}
26
26
  # @return [Mysql2::EM::Client]
27
27
  def emdatabase
@@ -29,17 +29,36 @@ module EzNemo
29
29
  end
30
30
 
31
31
  # Returns all active checks
32
- # @return [Array<Hash, ...>]
32
+ # @return [Array<EzNemo::Check, ...>]
33
33
  def checks
34
- q = 'SELECT * FROM checks WHERE state = true'
35
- database.query(q, {symbolize_keys: true, cast_booleans: true})
34
+ checks_with_tags(EzNemo.config[:checks][:tags])
35
+ end
36
+
37
+ # Returns all active checks matching all tags
38
+ # @param tags [Array<String, ...>] list of tag text
39
+ # @return [Array<EzNemo::Checks, ...>]
40
+ def checks_with_tags(tags = nil)
41
+ cids = check_ids_with_tags(tags)
42
+ return Check.where(state: true).all if cids.nil?
43
+ Check.where(state: true, id: cids).all
44
+ end
45
+
46
+ # @param tags [Array<String, ...>] list of tag text
47
+ # @return [Array] check_id mathing all tags; nil when no tags supplied
48
+ def check_ids_with_tags(tags = nil)
49
+ return nil if tags.nil? || tags.empty?
50
+ candi_ids = []
51
+ tags.each { |t| candi_ids << Tag.where(text: t).map(:check_id) }
52
+ final_ids = candi_ids[0]
53
+ candi_ids.each { |ids| final_ids = final_ids & ids }
54
+ final_ids
36
55
  end
37
56
 
38
57
  # Stores a result; into queue first
39
58
  # @param result [Hash] (see {EzNemo::Monitor#report})
40
59
  def store_result(result)
41
60
  @results << result
42
- if @results.count >= QUEUE_SIZE
61
+ if @results.count >= @queue_size
43
62
  write_results
44
63
  end
45
64
  end
@@ -49,19 +68,28 @@ module EzNemo
49
68
  # @return [Object] Mysql2 client instance
50
69
  def write_results(sync = false)
51
70
  return nil if @results.empty?
52
- sync ? db = database : db = emdatabase
53
- stmt = ''
54
- @results.each do |r|
55
- r[:probe] = @probe
56
- r[:status_desc] = db.escape(r[:status_desc])
57
- cols = r.keys.join(',')
58
- vals = r.values.join("','")
59
- stmt << "INSERT INTO results (#{cols}) VALUES ('#{vals}');"
60
- end
61
- @results.clear
62
71
  if sync
63
- db.query(stmt)
72
+ # Sequel won't run after trap; run in another thread
73
+ thr = Thread.new do
74
+ puts 'Flushing in another thread...'
75
+ Result.db.transaction do
76
+ @results.each { |r| r.save}
77
+ end
78
+ end
79
+ thr.join
80
+ return true
64
81
  else
82
+ db = emdatabase
83
+ stmt = ''
84
+ @results.each do |r|
85
+ # r[:probe] = @probe
86
+ r[:status_desc] = db.escape(r[:status_desc])
87
+ cols = r.values.keys.join(',')
88
+ # find and convert boolean values to integer
89
+ vals = r.values.values.map { |v| !!v == v ? (v ? 1 : 0) : v }
90
+ vals = vals.join("','")
91
+ stmt << "INSERT INTO results (#{cols}) VALUES ('#{vals}');"
92
+ end
65
93
  defer = db.query(stmt)
66
94
  defer.callback do
67
95
  end
@@ -70,6 +98,7 @@ module EzNemo
70
98
  db.close if db.ping
71
99
  end
72
100
  end
101
+ @results.clear
73
102
  db
74
103
  end
75
104
 
@@ -1,5 +1,5 @@
1
1
  module EzNemo
2
2
 
3
- Version = '0.1.0'
3
+ Version = '0.2.0'
4
4
 
5
5
  end
data/lib/eznemo.rb CHANGED
@@ -32,7 +32,7 @@ module EzNemo
32
32
  ds = EzNemo.datastore
33
33
 
34
34
  Signal.trap('INT') do
35
- puts 'Interrupted. Flushing...'
35
+ puts 'Interrupted.'
36
36
  ds.flush
37
37
  exit
38
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eznemo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken J.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-05 00:00:00.000000000 Z
11
+ date: 2016-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kajiki
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sequel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.0'
41
55
  description: Simple network monitoring
42
56
  email:
43
57
  - kenjij@gmail.com