eznemo 0.1.0 → 0.2.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.
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