cognizant 0.0.1 → 0.0.2

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.
@@ -4,6 +4,7 @@ require "cognizant"
4
4
  require "cognizant/logging"
5
5
  require "cognizant/process"
6
6
  require "cognizant/server/interface"
7
+ require "cognizant/system"
7
8
 
8
9
  module Cognizant
9
10
  module Server
@@ -85,9 +86,9 @@ module Cognizant
85
86
  # @return [String] Defaults to nil
86
87
  attr_accessor :group
87
88
 
88
- # Array of processes being managed.
89
+ # Hash of processes being managed.
89
90
  # @private
90
- # @return [Array]
91
+ # @return [Hash]
91
92
  attr_accessor :processes
92
93
 
93
94
  # Initializes and starts the cognizant daemon with the given options
@@ -121,10 +122,10 @@ module Cognizant
121
122
  @pids_dir = File.expand_path(@pids_dir)
122
123
  @logs_dir = File.expand_path(@logs_dir)
123
124
 
124
- self.processes = {}
125
+ self.processes = Hash.new
125
126
 
126
- # # Only available through a config file/stdin.
127
- # load_processes(options[:monitor]) if options.has_key?(:monitor)
127
+ # Only available through a config file/stdin.
128
+ load_processes(options[:monitor]) if options.has_key?(:monitor)
128
129
  end
129
130
 
130
131
  def bootup
@@ -134,7 +135,8 @@ module Cognizant
134
135
  EventMachine.run do
135
136
  start_interface_server
136
137
  start_periodic_ticks
137
- daemonize
138
+ daemonize_process
139
+ write_pid
138
140
  end
139
141
  end
140
142
 
@@ -149,14 +151,13 @@ module Cognizant
149
151
 
150
152
  def monitor(process_name = nil, attributes = {}, &block)
151
153
  process = Cognizant::Process.new(process_name, attributes, &block)
152
- self.processes[process_name] = process
154
+ self.processes[process.name] = process
153
155
  process.monitor
154
156
  end
155
157
 
156
158
  # Stops the TCP server and the tick loop, and performs cleanup.
157
159
  def shutdown
158
160
  log.info "Shutting down cognizantd..."
159
-
160
161
  EventMachine.next_tick do
161
162
  EventMachine.stop
162
163
  logger.close
@@ -175,13 +176,14 @@ module Cognizant
175
176
 
176
177
  # Starts the TCP server with the set socket lock file or port.
177
178
  def start_interface_server
179
+ stop_previous_server
178
180
  if port = @port
179
181
  log.info "Starting the TCP server at #{@port}..."
180
182
  host = "127.0.0.1"
181
183
  splitted = port.to_s.split(":")
182
184
  host, port = splitted if splitted.size > 1
183
185
  EventMachine.start_server(host, port, Server::Interface)
184
- else
186
+ else
185
187
  log.info "Starting the UNIX domain server with socket #{@socket}..."
186
188
  EventMachine.start_unix_domain_server(@socket, Server::Interface)
187
189
  end
@@ -191,9 +193,8 @@ module Cognizant
191
193
  def start_periodic_ticks
192
194
  log.info "Starting the periodic tick..."
193
195
  EventMachine.add_periodic_timer(1) do
194
- self.processes.each do |group, process|
195
- process.tick
196
- end
196
+ System.reset_data
197
+ self.processes.values.map(&:tick)
197
198
  end
198
199
  end
199
200
 
@@ -220,18 +221,55 @@ module Cognizant
220
221
  Signal.trap('QUIT', &terminator)
221
222
  end
222
223
 
224
+ def stop_previous_server
225
+ if @pidfile and File.exists?(@pidfile)
226
+ if previous_daemon_pid = File.read(@pidfile).to_i
227
+ # Only attempt to stop automatically if the daemon will run in background.
228
+ if @daemonize and System.pid_running?(previous_daemon_pid)
229
+ # Ensure that the process stops within 5 seconds or force kill.
230
+ signals = ["TERM", "KILL"]
231
+ timeout = 2
232
+ catch :stopped do
233
+ signals.each do |stop_signal|
234
+ # Send the stop signal and wait for it to stop.
235
+ System.signal(stop_signal, previous_daemon_pid)
236
+
237
+ # Poll to see if it's stopped yet. Minimum 2 so that we check at least once again.
238
+ ([timeout / signals.size, 2].max).times do
239
+ throw :stopped unless System.pid_running?(previous_daemon_pid)
240
+ sleep 1
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ # Alert the user to manually stop the previous daemon, if it is [still] alive.
248
+ if System.pid_running?(previous_daemon_pid)
249
+ raise "There is already a daemon running with pid #{previous_daemon_pid}."
250
+ else
251
+ begin
252
+ File.unlink(@pidfile) if @pidfile
253
+ rescue Errno::ENOENT
254
+ nil
255
+ end
256
+ end
257
+ end
258
+ end
259
+
223
260
  # Daemonize the current process and save it pid in a file.
224
- def daemonize
261
+ def daemonize_process
225
262
  if @daemonize
226
263
  log.info "Daemonizing into the background..."
227
264
  ::Process.daemon
265
+ end
266
+ end
228
267
 
229
- pid = ::Process.pid
230
-
231
- if @pidfile
232
- log.info "Writing the daemon pid (#{pid}) to the pidfile..."
233
- File.open(@pidfile, "w") { |f| f.write(pid) }
234
- end
268
+ def write_pid
269
+ pid = ::Process.pid
270
+ if @pidfile
271
+ log.info "Writing the daemon pid (#{pid}) to the pidfile..."
272
+ File.open(@pidfile, "w") { |f| f.write(pid) }
235
273
  end
236
274
  end
237
275
  end
@@ -1,4 +1,3 @@
1
- require "json"
2
1
  require "eventmachine"
3
2
 
4
3
  require "cognizant/server/commands"
@@ -6,55 +5,82 @@ require "cognizant/server/commands"
6
5
  module Cognizant
7
6
  module Server
8
7
  class Interface < EventMachine::Connection
8
+
9
+ attr_accessor :authenticated, :username, :password
10
+
9
11
  def post_init
10
- # puts "-- someone connected to the server!"
12
+ @authenticated = false
13
+ @username = nil
14
+ @password = nil
15
+ if Cognizant::Server.daemon.username and Cognizant::Server.daemon.password
16
+ post_authentication_challenge
17
+ end
11
18
  end
12
19
 
13
20
  def receive_data(args)
21
+ if not @authenticated and (Cognizant::Server.daemon.username and Cognizant::Server.daemon.password)
22
+ return post_authentication_challenge(args.to_s)
23
+ end
24
+
14
25
  args = [*args.split]
15
26
  command = args.shift.to_s.strip.downcase
16
27
  if command.size > 0
17
28
  begin
18
29
  Commands.send(command, *args) do |response|
19
- send_data "#{response.to_json}\r\n\r\n"
30
+ send_data "#{response}"
20
31
  end
21
32
  rescue => e
22
- send_data "#{e.inspect.to_json}\r\n\r\n"
33
+ send_data "#{e.message}"
23
34
  end
24
35
  end
25
- # close_connection_after_writing
36
+ close_connection_after_writing
26
37
  end
27
38
 
28
39
  def unbind
40
+ @authenticated = false
29
41
  # puts "-- someone disconnected from the server!"
30
42
  end
43
+
44
+ def post_authentication_challenge(data = nil)
45
+ data = data.to_s.strip
46
+ if data.to_s.size > 0
47
+ if @username
48
+ @password = data
49
+ else
50
+ @username = data
51
+ end
52
+ end
53
+
54
+ unless @username.to_s.size > 0
55
+ if Cognizant::Server.daemon.username and Cognizant::Server.daemon.username.size != nil
56
+ send_data "Username: "
57
+ return
58
+ end
59
+ end
60
+
61
+ unless @password.to_s.size > 0
62
+ if Cognizant::Server.daemon.password and Cognizant::Server.daemon.password.size != nil
63
+ send_data "Password: "
64
+ return
65
+ end
66
+ end
67
+
68
+ if @username and @password
69
+ validate
70
+ end
71
+ end
72
+
73
+ def validate
74
+ @authenticated = @username == Cognizant::Server.daemon.username and @password == Cognizant::Server.daemon.password
75
+ @username = nil
76
+ @password = nil
77
+ if @authenticated
78
+ send_data "Authenticated. Welcome!\r\n\r\n"
79
+ else
80
+ send_data "Authentication failed.\r\n\r\n"
81
+ post_authentication_challenge
82
+ end
83
+ end
31
84
  end
32
85
  end
33
86
  end
34
-
35
- # module Cognizant
36
- # module Server
37
- # class Interface < EventMachine::Connection
38
- # include EventMachine::Protocols::ObjectProtocol
39
- # # include EventMachine::Protocols::SASLauth
40
- #
41
- # def post_init
42
- # @obj = Hash.new
43
- # puts "-- someone connected to the echo server!"
44
- # end
45
- #
46
- # def receive_object(method)
47
- # send_object @obj.send(*method)
48
- # end
49
- #
50
- # def unbind
51
- # @obj = nil
52
- # puts "-- someone disconnected from the echo server!"
53
- # end
54
- #
55
- # # def validate(usr, psw, sys, realm)
56
- # # usr == TestUser and psw == TestPsw
57
- # # end
58
- # end
59
- # end
60
- # end
@@ -0,0 +1,61 @@
1
+ module Cognizant
2
+ module System
3
+ module PS
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ # Fields to fetch from ps.
10
+ IDX_MAP = {
11
+ :pid => 0,
12
+ :ppid => 1,
13
+ :pcpu => 2,
14
+ :rss => 3
15
+ }
16
+
17
+ def cpu_usage(pid)
18
+ ps_axu[pid] && ps_axu[pid][IDX_MAP[:pcpu]].to_f
19
+ end
20
+
21
+ def memory_usage(pid)
22
+ ps_axu[pid] && ps_axu[pid][IDX_MAP[:rss]].to_f
23
+ end
24
+
25
+ def get_children(parent_pid)
26
+ child_pids = Array.new
27
+ ps_axu.each_pair do |pid, chunks|
28
+ child_pids << chunks[IDX_MAP[:pid]].to_i if chunks[IDX_MAP[:ppid]].to_i == parent_pid.to_i
29
+ end
30
+ child_pids
31
+ end
32
+
33
+ def reset_data
34
+ store.clear unless store.empty?
35
+ end
36
+
37
+ def ps_axu
38
+ # TODO: Need a mutex here?
39
+ store[:ps_axu] ||= begin
40
+ # BSD style ps invocation.
41
+ lines = `ps axo #{IDX_MAP.keys.join(",")}`.split("\n")
42
+
43
+ lines.inject(Hash.new) do |mem, line|
44
+ chunks = line.split(/\s+/)
45
+ chunks.delete_if {|c| c.strip.empty? }
46
+ pid = chunks[IDX_MAP[:pid]].strip.to_i
47
+ mem[pid] = chunks
48
+ mem
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def store
56
+ @store ||= Hash.new
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,37 @@
1
+ module Cognizant
2
+ module System
3
+ module Signal
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def signal(signal, pid)
10
+ ::Process.kill(signal, pid) if pid and pid != 0
11
+ end
12
+
13
+ def send_signals(pid, options = {})
14
+ # Return if the process is not running.
15
+ return true unless pid_running?(pid)
16
+
17
+ signals = options[:signals] || ["TERM", "INT", "KILL"]
18
+ timeout = options[:timeout] || 10
19
+
20
+ catch :stopped do
21
+ signals.each do |stop_signal|
22
+ # Send the stop signal and wait for it to stop.
23
+ signal(stop_signal, pid)
24
+
25
+ # Poll to see if it's stopped yet. Minimum 2 so that we check at least once again.
26
+ ([timeout / signals.size, 2].max).times do
27
+ throw :stopped unless pid_running?(pid)
28
+ sleep 1
29
+ end
30
+ end
31
+ end
32
+ not pid_running?(pid)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ require "cognizant/system/signal"
2
+ require "cognizant/system/ps"
3
+
4
+ module Cognizant
5
+ module System
6
+ extend self
7
+
8
+ include Cognizant::System::Signal
9
+ include Cognizant::System::PS
10
+
11
+ def pid_running?(pid)
12
+ return false unless pid and pid != 0
13
+ signal(0, pid)
14
+ # It's running since no exception was raised.
15
+ true
16
+ rescue Errno::ESRCH
17
+ # No such process.
18
+ false
19
+ rescue Errno::EPERM
20
+ # Probably running, but we're not allowed to pass signals.
21
+ # TODO: Is this a sign of problems ahead?
22
+ true
23
+ else
24
+ # Possibly running.
25
+ true
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module Cognizant
2
+ module Util
3
+ class RotationalArray < Array
4
+ def initialize(size)
5
+ @capacity = size
6
+ super # no size - intentionally
7
+ end
8
+
9
+ def push(value)
10
+ super(value)
11
+ self.shift if self.length > @capacity
12
+ self
13
+ end
14
+ alias_method :<<, :push
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ class Hash
2
+ # Destructively convert all keys by using the block operation.
3
+ # This includes the keys from the root hash and from all
4
+ # nested hashes.
5
+ def deep_transform_keys!(&block)
6
+ keys.each do |key|
7
+ value = delete(key)
8
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
9
+ end
10
+ self
11
+ end
12
+
13
+ # Destructively convert all keys to symbols, as long as they respond
14
+ # to +to_sym+. This includes the keys from the root hash and from all
15
+ # nested hashes.
16
+ def deep_symbolize_keys!
17
+ deep_transform_keys!{ |key| key.to_sym rescue key }
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Cognizant
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/cognizant.rb CHANGED
@@ -1,7 +1,10 @@
1
+ require "active_support/core_ext/numeric"
2
+ require "active_support/duration"
3
+
1
4
  require "cognizant/server"
2
5
 
3
6
  module Cognizant
4
- def self.monitor(process_name, &block)
5
- Cognizant::Server.daemon.monitor(process_name, &block)
7
+ def self.monitor(process_name = nil, attributes = {}, &block)
8
+ Cognizant::Server.daemon.monitor(process_name, attributes, &block)
6
9
  end
7
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cognizant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-01 00:00:00.000000000 Z
12
+ date: 2012-11-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -91,7 +91,56 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
- description: Process monitoring and management framework
94
+ - !ruby/object:Gem::Dependency
95
+ name: activesupport
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: gli
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: formatador
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: Cognizant is a process management system utility that supervises your
143
+ processes, ensuring their state based on a flexible criteria.
95
144
  email:
96
145
  - contact@gurpartap.com
97
146
  executables:
@@ -116,8 +165,6 @@ files:
116
165
  - images/logo.png
117
166
  - images/logo.pxm
118
167
  - lib/cognizant.rb
119
- - lib/cognizant/client/cli.rb
120
- - lib/cognizant/client/interface.rb
121
168
  - lib/cognizant/logging.rb
122
169
  - lib/cognizant/process.rb
123
170
  - lib/cognizant/process/actions.rb
@@ -125,6 +172,14 @@ files:
125
172
  - lib/cognizant/process/actions/start.rb
126
173
  - lib/cognizant/process/actions/stop.rb
127
174
  - lib/cognizant/process/attributes.rb
175
+ - lib/cognizant/process/condition_check.rb
176
+ - lib/cognizant/process/conditions.rb
177
+ - lib/cognizant/process/conditions/always_true.rb
178
+ - lib/cognizant/process/conditions/cpu_usage.rb
179
+ - lib/cognizant/process/conditions/flapping.rb
180
+ - lib/cognizant/process/conditions/memory_usage.rb
181
+ - lib/cognizant/process/conditions/poll_condition.rb
182
+ - lib/cognizant/process/conditions/trigger_condition.rb
128
183
  - lib/cognizant/process/execution.rb
129
184
  - lib/cognizant/process/pid.rb
130
185
  - lib/cognizant/process/status.rb
@@ -132,9 +187,14 @@ files:
132
187
  - lib/cognizant/server/commands.rb
133
188
  - lib/cognizant/server/daemon.rb
134
189
  - lib/cognizant/server/interface.rb
190
+ - lib/cognizant/system.rb
191
+ - lib/cognizant/system/ps.rb
192
+ - lib/cognizant/system/signal.rb
193
+ - lib/cognizant/util/rotational_array.rb
194
+ - lib/cognizant/util/symbolize_hash_keys.rb
135
195
  - lib/cognizant/validations.rb
136
196
  - lib/cognizant/version.rb
137
- homepage: http://gurpartap.github.com/cognizant/
197
+ homepage: http://github.com/Gurpartap/cognizant
138
198
  licenses: []
139
199
  post_install_message:
140
200
  rdoc_options: []
@@ -148,7 +208,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
208
  version: '0'
149
209
  segments:
150
210
  - 0
151
- hash: 2933141880858945843
211
+ hash: -687515942623630851
152
212
  required_rubygems_version: !ruby/object:Gem::Requirement
153
213
  none: false
154
214
  requirements:
@@ -157,13 +217,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
217
  version: '0'
158
218
  segments:
159
219
  - 0
160
- hash: 2933141880858945843
220
+ hash: -687515942623630851
161
221
  requirements: []
162
222
  rubyforge_project:
163
223
  rubygems_version: 1.8.24
164
224
  signing_key:
165
225
  specification_version: 3
166
- summary: Cognizant is an advanced and efficient process monitoring and management
167
- framework.
226
+ summary: Cognizant is a process management system utility that supervises your processes,
227
+ ensuring their state based on a flexible criteria.
168
228
  test_files: []
169
229
  has_rdoc:
@@ -1,9 +0,0 @@
1
- module Cognizant
2
- module Client
3
- class CLI
4
- def initialize(command)
5
- puts "got #{command}"
6
- end
7
- end
8
- end
9
- end
@@ -1,33 +0,0 @@
1
- require "eventmachine"
2
-
3
- module Cognizant
4
- module Client
5
- class Interface
6
- def initialize(server = nil)
7
- @sock = EventMachine.connect(server, Client::Interface::Handler)
8
- end
9
-
10
- def method_missing(*meth, &blk)
11
- @sock.queue << blk
12
- @sock.send_object(meth)
13
- end
14
-
15
- class Handler < EventMachine::Connection
16
- # include EventMachine::Protocols::SASLauthclient
17
- include EventMachine::Protocols::ObjectProtocol
18
-
19
- attr_reader :queue
20
-
21
- def post_init
22
- @queue = []
23
- end
24
-
25
- def receive_object(obj)
26
- if callback = @queue.shift
27
- callback.call(obj)
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end