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.
- data/.gitignore +2 -1
- data/README.md +118 -57
- data/bin/cognizant +156 -36
- data/bin/cognizantd +19 -9
- data/cognizant.gemspec +9 -3
- data/examples/cognizantd.yml +37 -10
- data/examples/redis-server.rb +19 -8
- data/lib/cognizant/process/actions.rb +3 -17
- data/lib/cognizant/process/condition_check.rb +59 -0
- data/lib/cognizant/process/conditions/always_true.rb +15 -0
- data/lib/cognizant/process/conditions/cpu_usage.rb +21 -0
- data/lib/cognizant/process/conditions/flapping.rb +57 -0
- data/lib/cognizant/process/conditions/memory_usage.rb +32 -0
- data/lib/cognizant/process/conditions/poll_condition.rb +23 -0
- data/lib/cognizant/process/conditions/trigger_condition.rb +52 -0
- data/lib/cognizant/process/conditions.rb +16 -0
- data/lib/cognizant/process/execution.rb +5 -5
- data/lib/cognizant/process/pid.rb +4 -2
- data/lib/cognizant/process/status.rb +4 -16
- data/lib/cognizant/process.rb +92 -12
- data/lib/cognizant/server/commands.rb +50 -23
- data/lib/cognizant/server/daemon.rb +57 -19
- data/lib/cognizant/server/interface.rb +58 -32
- data/lib/cognizant/system/ps.rb +61 -0
- data/lib/cognizant/system/signal.rb +37 -0
- data/lib/cognizant/system.rb +28 -0
- data/lib/cognizant/util/rotational_array.rb +17 -0
- data/lib/cognizant/util/symbolize_hash_keys.rb +19 -0
- data/lib/cognizant/version.rb +1 -1
- data/lib/cognizant.rb +5 -2
- metadata +70 -10
- data/lib/cognizant/client/cli.rb +0 -9
- data/lib/cognizant/client/interface.rb +0 -33
@@ -1,6 +1,7 @@
|
|
1
1
|
require "cognizant/process/actions/start"
|
2
2
|
require "cognizant/process/actions/stop"
|
3
3
|
require "cognizant/process/actions/restart"
|
4
|
+
require "cognizant/system"
|
4
5
|
|
5
6
|
module Cognizant
|
6
7
|
class Process
|
@@ -55,7 +56,7 @@ module Cognizant
|
|
55
56
|
end
|
56
57
|
sleep 1
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
# Kill the nested thread.
|
60
61
|
thread.kill
|
61
62
|
|
@@ -73,22 +74,7 @@ module Cognizant
|
|
73
74
|
def send_signals(options = {})
|
74
75
|
# Return if the process is already stopped.
|
75
76
|
return true unless pid_running?
|
76
|
-
|
77
|
-
signals = options[:signals] || ["TERM", "INT", "KILL"]
|
78
|
-
timeout = options[:timeout] || 10
|
79
|
-
|
80
|
-
catch :stopped do
|
81
|
-
signals.each do |stop_signal|
|
82
|
-
# Send the stop signal and wait for it to stop.
|
83
|
-
signal(stop_signal, @process_pid)
|
84
|
-
|
85
|
-
# Poll to see if it's stopped yet. Minimum 2 so that we check at least once again.
|
86
|
-
([timeout / signals.size, 2].max).times do
|
87
|
-
throw :stopped unless pid_running?
|
88
|
-
sleep 1
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
77
|
+
Cognizant::System.send_signals(@process_pid, options)
|
92
78
|
not pid_running?
|
93
79
|
end
|
94
80
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "cognizant/process/conditions"
|
2
|
+
require "cognizant/util/rotational_array"
|
3
|
+
|
4
|
+
module Cognizant
|
5
|
+
class Process
|
6
|
+
class ConditionCheck
|
7
|
+
class HistoryValue < Struct.new(:value, :critical); end
|
8
|
+
|
9
|
+
# No need to recreate one every tick.
|
10
|
+
EMPTY_ARRAY = [].freeze
|
11
|
+
|
12
|
+
attr_accessor :condition_name
|
13
|
+
def initialize(condition_name, options = {}, &block)
|
14
|
+
@condition_name = condition_name
|
15
|
+
|
16
|
+
if block
|
17
|
+
@do = Array(block)
|
18
|
+
else
|
19
|
+
@do = options.has_key?(:do) ? Array(options.delete(:do)) : [:restart]
|
20
|
+
end
|
21
|
+
|
22
|
+
@every = options.delete(:every).to_i
|
23
|
+
@times = options.delete(:times) || [1, 1]
|
24
|
+
@times = [@times, @times] unless @times.is_a?(Array) # handles :times => 5
|
25
|
+
@times.map(&:to_i)
|
26
|
+
|
27
|
+
clear_history!
|
28
|
+
|
29
|
+
@condition = Cognizant::Process::Conditions[@condition_name].new(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(pid, tick_number = Time.now.to_i)
|
33
|
+
if @last_ran_at.nil? || (@last_ran_at + @every) <= tick_number
|
34
|
+
@last_ran_at = tick_number
|
35
|
+
|
36
|
+
value = @condition.run(pid)
|
37
|
+
@history << HistoryValue.new(@condition.format_value(value), @condition.check(value))
|
38
|
+
# puts self.to_s
|
39
|
+
|
40
|
+
return @do if failed_check?
|
41
|
+
end
|
42
|
+
EMPTY_ARRAY
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear_history!
|
46
|
+
@history = Util::RotationalArray.new(@times.last)
|
47
|
+
end
|
48
|
+
|
49
|
+
def failed_check?
|
50
|
+
@history.count { |v| v and v.critical } >= @times.first
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
data = @history.collect { |v| v and "#{v.value}#{'*' unless v.critical}" }.join(", ")
|
55
|
+
"#{@condition_name}: [#{data}]\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "cognizant/system"
|
2
|
+
|
3
|
+
module Cognizant
|
4
|
+
class Process
|
5
|
+
module Conditions
|
6
|
+
class CpuUsage < PollCondition
|
7
|
+
def initialize(options = {})
|
8
|
+
@above = options[:above].to_f
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(pid)
|
12
|
+
System.cpu_usage(pid).to_f
|
13
|
+
end
|
14
|
+
|
15
|
+
def check(value)
|
16
|
+
value > @above
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "cognizant/util/rotational_array"
|
2
|
+
|
3
|
+
module Cognizant
|
4
|
+
class Process
|
5
|
+
module Conditions
|
6
|
+
class Flapping < TriggerCondition
|
7
|
+
TRIGGER_STATES = [:starting, :restarting]
|
8
|
+
|
9
|
+
attr_accessor :times, :within, :retry_after
|
10
|
+
attr_reader :timeline
|
11
|
+
|
12
|
+
def initialize(process, options = {})
|
13
|
+
options = { :times => 5, :within => 1, :retry_after => 5 }.merge(options)
|
14
|
+
|
15
|
+
options.each_pair do |name, value|
|
16
|
+
self.send("#{name}=", value) if self.respond_to?("#{name}=")
|
17
|
+
end
|
18
|
+
|
19
|
+
@timeline = Util::RotationalArray.new(@times)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def notify(transition)
|
24
|
+
if TRIGGER_STATES.include?(transition.to_name)
|
25
|
+
self.timeline << Time.now.to_i
|
26
|
+
self.check_flapping
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset!
|
31
|
+
@timeline.clear
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_flapping
|
36
|
+
# The process has not flapped if we haven't encountered enough incidents.
|
37
|
+
return unless (@timeline.compact.length == self.times)
|
38
|
+
|
39
|
+
# Check if the incident happend within the timeframe.
|
40
|
+
duration = (@timeline.last - @timeline.first) <= self.within
|
41
|
+
|
42
|
+
if duration
|
43
|
+
puts "Flapping detected: retrying in #{self.retry_after} seconds"
|
44
|
+
|
45
|
+
self.schedule_event(:start, self.retry_after) unless self.retry_after == 0 # retry_after zero means "do not retry, ever".
|
46
|
+
self.schedule_event(:unmonitor, 0)
|
47
|
+
|
48
|
+
@timeline.clear
|
49
|
+
|
50
|
+
# This will prevent a transition from happening in the process state_machine.
|
51
|
+
throw :halt
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Cognizant
|
2
|
+
class Process
|
3
|
+
module Conditions
|
4
|
+
class MemoryUsage < PollCondition
|
5
|
+
MB = 1024 ** 2
|
6
|
+
FORMAT_STR = "%d%s"
|
7
|
+
MB_LABEL = "MB"
|
8
|
+
KB_LABEL = "KB"
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@above = options[:above].to_f
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(pid)
|
15
|
+
System.memory_usage(pid).to_f
|
16
|
+
end
|
17
|
+
|
18
|
+
def check(value)
|
19
|
+
value.kilobytes > @above
|
20
|
+
end
|
21
|
+
|
22
|
+
def format_value(value)
|
23
|
+
if value.kilobytes >= MB
|
24
|
+
FORMAT_STR % [(value / 1024).round, MB_LABEL]
|
25
|
+
else
|
26
|
+
FORMAT_STR % [value, KB_LABEL]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cognizant
|
2
|
+
class Process
|
3
|
+
module Conditions
|
4
|
+
class PollCondition
|
5
|
+
def initialize(options = {})
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(pid)
|
10
|
+
raise "Implement in subclass!"
|
11
|
+
end
|
12
|
+
|
13
|
+
def check(value)
|
14
|
+
raise "Implement in subclass!"
|
15
|
+
end
|
16
|
+
|
17
|
+
def format_value(value)
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Cognizant
|
2
|
+
class Process
|
3
|
+
module Conditions
|
4
|
+
class TriggerCondition
|
5
|
+
attr_accessor :process, :mutex, :scheduled_events
|
6
|
+
|
7
|
+
def initialize(process, options = {})
|
8
|
+
self.process = process
|
9
|
+
self.mutex = Mutex.new
|
10
|
+
self.scheduled_events = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset!
|
14
|
+
self.cancel_all_events
|
15
|
+
end
|
16
|
+
|
17
|
+
def notify(transition)
|
18
|
+
raise "Implement in subclass"
|
19
|
+
end
|
20
|
+
|
21
|
+
def dispatch!(event)
|
22
|
+
self.process.dispatch!(event, self.class.name.split("::").last)
|
23
|
+
end
|
24
|
+
|
25
|
+
def schedule_event(event, delay)
|
26
|
+
# TODO: Maybe wrap this in a ScheduledEvent class with methods like cancel.
|
27
|
+
thread = Thread.new(self) do |trigger|
|
28
|
+
begin
|
29
|
+
sleep delay.to_f
|
30
|
+
trigger.dispatch!(event)
|
31
|
+
trigger.mutex.synchronize do
|
32
|
+
trigger.scheduled_events.delete_if { |_, thread| thread == Thread.current }
|
33
|
+
end
|
34
|
+
rescue StandardError => e
|
35
|
+
puts(e)
|
36
|
+
puts(e.backtrace.join("\n"))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
self.scheduled_events.push([event, thread])
|
41
|
+
end
|
42
|
+
|
43
|
+
def cancel_all_events
|
44
|
+
puts "Canceling all scheduled events"
|
45
|
+
self.mutex.synchronize do
|
46
|
+
self.scheduled_events.each {|_, thread| thread.kill}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "cognizant/process/conditions/poll_condition"
|
2
|
+
require "cognizant/process/conditions/trigger_condition"
|
3
|
+
|
4
|
+
Dir["#{File.dirname(__FILE__)}/conditions/*.rb"].each do |c|
|
5
|
+
require c
|
6
|
+
end
|
7
|
+
|
8
|
+
module Cognizant
|
9
|
+
class Process
|
10
|
+
module Conditions
|
11
|
+
def self.[](name)
|
12
|
+
const_get(name.to_s.camelcase)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -65,11 +65,11 @@ module Cognizant
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# Merge spawn options.
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
spawn_options = construct_spawn_options(options, {
|
69
|
+
:in => stdin,
|
70
|
+
:out => stdout,
|
71
|
+
:err => stderr
|
72
|
+
})
|
73
73
|
|
74
74
|
# Spawn a process to execute the command.
|
75
75
|
process_pid = ::Process.spawn(options[:env], command, spawn_options)
|
@@ -4,11 +4,13 @@ module Cognizant
|
|
4
4
|
def read_pid
|
5
5
|
if self.pid_command
|
6
6
|
str = execute(self.pid_command).stdout.to_i
|
7
|
-
@process_pid = str unless not str or str.zero?
|
7
|
+
@process_pid = str unless not str or str.zero?
|
8
|
+
# TODO: Write pid to pidfile, since our source was pid_command instead.
|
8
9
|
elsif self.pidfile and File.exists?(self.pidfile)
|
9
10
|
str = File.read(self.pidfile).to_i
|
10
|
-
@process_pid = str unless not str or str.zero?
|
11
|
+
@process_pid = str unless not str or str.zero?
|
11
12
|
end
|
13
|
+
@process_pid = 0 unless System.pid_running?(@process_pid)
|
12
14
|
@process_pid
|
13
15
|
end
|
14
16
|
|
@@ -1,26 +1,14 @@
|
|
1
|
+
require "cognizant/system"
|
2
|
+
|
1
3
|
module Cognizant
|
2
4
|
class Process
|
3
5
|
module Status
|
4
6
|
def pid_running?
|
5
|
-
|
6
|
-
return false unless pid and pid != 0
|
7
|
-
signal(0, pid)
|
8
|
-
# It's running since no exception was raised.
|
9
|
-
true
|
10
|
-
rescue Errno::ESRCH
|
11
|
-
# No such process.
|
12
|
-
false
|
13
|
-
rescue Errno::EPERM
|
14
|
-
# Probably running, but we're not allowed to pass signals.
|
15
|
-
# TODO: Is this a sign of problems ahead?
|
16
|
-
true
|
17
|
-
else
|
18
|
-
# Possibly running.
|
19
|
-
true
|
7
|
+
Cognizant::System.pid_running?(read_pid)
|
20
8
|
end
|
21
9
|
|
22
10
|
def signal(signal, pid = nil)
|
23
|
-
::
|
11
|
+
Cognizant::System.signal(signal, (pid || read_pid))
|
24
12
|
end
|
25
13
|
end
|
26
14
|
end
|
data/lib/cognizant/process.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
require
|
1
|
+
require "monitor"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
require "state_machine"
|
2
5
|
|
3
6
|
require "cognizant/process/pid"
|
4
7
|
require "cognizant/process/status"
|
5
8
|
require "cognizant/process/execution"
|
6
9
|
require "cognizant/process/attributes"
|
7
10
|
require "cognizant/process/actions"
|
11
|
+
require "cognizant/process/condition_check"
|
12
|
+
require "cognizant/util/symbolize_hash_keys"
|
8
13
|
|
9
14
|
module Cognizant
|
10
15
|
class Process
|
@@ -66,21 +71,30 @@ module Cognizant
|
|
66
71
|
before_transition any => :restarting, :do => lambda { |p| p.autostart = true }
|
67
72
|
after_transition any => :restarting, :do => :restart_process
|
68
73
|
|
69
|
-
before_transition any => any, :do => :
|
70
|
-
after_transition any => any, :do => :
|
74
|
+
before_transition any => any, :do => :notify_triggers
|
75
|
+
after_transition any => any, :do => :record_transition
|
71
76
|
end
|
72
77
|
|
73
78
|
def initialize(process_name = nil, options = {})
|
74
|
-
|
75
|
-
|
76
|
-
|
79
|
+
@ticks_to_skip = 0
|
80
|
+
@checks = []
|
81
|
+
@triggers = []
|
82
|
+
@action_mutex = Monitor.new
|
83
|
+
|
84
|
+
self.name = process_name.to_s if process_name
|
85
|
+
self.autostart = true # Default.
|
86
|
+
|
87
|
+
if options.has_key?(:checks) and options[:checks].kind_of?(Hash)
|
88
|
+
options[:checks].each do |condition_name, args|
|
89
|
+
self.check(condition_name, args)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
options.delete(:checks)
|
77
93
|
|
78
94
|
options.each do |attribute_name, value|
|
79
95
|
self.send("#{attribute_name}=", value) if self.respond_to?("#{attribute_name}=")
|
80
96
|
end
|
81
97
|
|
82
|
-
@ticks_to_skip = 0
|
83
|
-
|
84
98
|
yield(self) if block_given?
|
85
99
|
|
86
100
|
# Let state_machine initialize as well.
|
@@ -93,14 +107,80 @@ module Cognizant
|
|
93
107
|
|
94
108
|
# Invoke the state_machine event.
|
95
109
|
super
|
110
|
+
|
111
|
+
self.run_checks if self.running?
|
112
|
+
end
|
113
|
+
|
114
|
+
def record_transition(transition)
|
115
|
+
unless transition.loopback?
|
116
|
+
@transitioned = true
|
117
|
+
@last_transition_time = Time.now.to_i
|
118
|
+
|
119
|
+
# When a process changes state, we should clear the memory of all the checks.
|
120
|
+
@checks.each { |check| check.clear_history! }
|
121
|
+
puts "#{name} changing from #{transition.from_name} => #{transition.to_name}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def last_transition_time
|
126
|
+
@last_transition_time || 0
|
127
|
+
end
|
128
|
+
|
129
|
+
def handle_user_command(command)
|
130
|
+
if command == :unmonitor
|
131
|
+
# When the user issues an unmonitor command, reset any
|
132
|
+
# triggers so that scheduled events gets cleared.
|
133
|
+
@triggers.each { |trigger| trigger.reset! }
|
134
|
+
end
|
135
|
+
dispatch!(command, "user initiated")
|
136
|
+
end
|
137
|
+
|
138
|
+
def dispatch!(action, reason = nil)
|
139
|
+
@action_mutex.synchronize do
|
140
|
+
if action.respond_to?(:call)
|
141
|
+
action.call(self)
|
142
|
+
else
|
143
|
+
self.send("#{action}")
|
144
|
+
end
|
145
|
+
end
|
96
146
|
end
|
97
147
|
|
98
|
-
def
|
99
|
-
|
148
|
+
def check(condition_name, options, &block)
|
149
|
+
klass = Cognizant::Process::Conditions[condition_name]
|
150
|
+
case klass.superclass.name.split("::").last
|
151
|
+
when "TriggerCondition"
|
152
|
+
@triggers << klass.new(self, options.deep_symbolize_keys!)
|
153
|
+
when "PollCondition"
|
154
|
+
@checks << ConditionCheck.new(condition_name, options.deep_symbolize_keys!, &block)
|
155
|
+
end
|
100
156
|
end
|
101
157
|
|
102
|
-
def
|
103
|
-
|
158
|
+
def notify_triggers(transition)
|
159
|
+
@triggers.each { |trigger| trigger.notify(transition) }
|
160
|
+
end
|
161
|
+
|
162
|
+
def run_checks
|
163
|
+
now = Time.now.to_i
|
164
|
+
|
165
|
+
threads = @checks.collect do |check|
|
166
|
+
[check, Thread.new { Thread.current[:actions] = check.run(read_pid, now) }]
|
167
|
+
end
|
168
|
+
|
169
|
+
@transitioned = false
|
170
|
+
|
171
|
+
threads.inject([]) do |actions, (check, thread)|
|
172
|
+
thread.join
|
173
|
+
if thread[:actions].size > 0
|
174
|
+
puts "#{check.condition_name} dispatched: #{thread[:actions].join(',')}"
|
175
|
+
thread[:actions].each do |action|
|
176
|
+
actions << [action, check.to_s]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
actions
|
180
|
+
end.each do |(action, reason)|
|
181
|
+
break if @transitioned
|
182
|
+
self.dispatch!(action, reason)
|
183
|
+
end
|
104
184
|
end
|
105
185
|
|
106
186
|
def process_running?
|
@@ -1,52 +1,79 @@
|
|
1
|
+
require "json"
|
2
|
+
require "cognizant/system"
|
3
|
+
|
1
4
|
module Cognizant
|
2
5
|
module Server
|
3
6
|
module Commands
|
4
7
|
def self.load(config_file)
|
5
8
|
Cognizant::Server.daemon.load(config_file)
|
6
|
-
yield("OK")
|
9
|
+
# yield("OK")
|
7
10
|
end
|
8
11
|
|
9
12
|
def self.status(*args)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
output_processes = []
|
14
|
+
if args.size > 0
|
15
|
+
Cognizant::Server.daemon.processes.values.each do |process|
|
16
|
+
output_processes << process if args.include?(process.name) or args.include?(process.group)
|
17
|
+
end
|
18
|
+
if output_processes.size == 0
|
19
|
+
raise("ERROR: No such process")
|
20
|
+
# yield("OK")
|
21
|
+
end
|
22
|
+
else
|
23
|
+
output_processes = Cognizant::Server.daemon.processes.values
|
19
24
|
end
|
20
|
-
|
25
|
+
|
26
|
+
output = []
|
27
|
+
output_processes.each do |process|
|
28
|
+
pid = process.read_pid
|
29
|
+
output << {
|
30
|
+
"Process" => process.name,
|
31
|
+
"PID" => pid,
|
32
|
+
"Group" => process.group,
|
33
|
+
"State" => process.state,
|
34
|
+
"Since" => process.last_transition_time,
|
35
|
+
"% CPU" => System.cpu_usage(pid).to_f,
|
36
|
+
"Memory" => System.memory_usage(pid).to_f # in KBs.
|
37
|
+
}
|
38
|
+
end
|
39
|
+
yield(output.to_json)
|
40
|
+
# yield("OK")
|
21
41
|
end
|
22
42
|
|
23
43
|
%w(monitor start stop restart unmonitor).each do |action|
|
24
44
|
class_eval <<-END
|
25
45
|
def self.#{action}(*args)
|
26
|
-
unless
|
27
|
-
|
28
|
-
return yield("OK")
|
46
|
+
unless args.size > 0
|
47
|
+
raise("ERROR: Missing process name")
|
48
|
+
return # yield("OK")
|
49
|
+
end
|
50
|
+
output_processes = []
|
51
|
+
Cognizant::Server.daemon.processes.values.each do |process|
|
52
|
+
if args.include?(process.name) or args.include?(process.group)
|
53
|
+
output_processes << process
|
54
|
+
end
|
29
55
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
56
|
+
|
57
|
+
if output_processes.size == 0
|
58
|
+
raise("ERROR: No such process")
|
59
|
+
# yield("OK")
|
60
|
+
else
|
61
|
+
output_processes.each do |process|
|
62
|
+
process.handle_user_command(:#{action})
|
34
63
|
end
|
35
64
|
end
|
36
|
-
yield("ERR: No such process")
|
37
|
-
yield("OK")
|
38
65
|
end
|
39
66
|
END
|
40
67
|
end
|
41
68
|
|
42
69
|
def self.shutdown
|
43
70
|
Cognizant::Server.daemon.shutdown
|
44
|
-
yield("OK")
|
71
|
+
# yield("OK")
|
45
72
|
end
|
46
73
|
|
47
74
|
def self.method_missing(command, *args)
|
48
|
-
|
49
|
-
yield("OK")
|
75
|
+
raise("ERROR: Unknown command '#{command}'")
|
76
|
+
# yield("OK")
|
50
77
|
end
|
51
78
|
end
|
52
79
|
end
|