more tests for task, real valid? method for task
[god.git] / lib / god / task.rb
blob4f148827521ec4903ce333de8e8da3a37e67759b
1 module God
2   
3   class Task
4     attr_accessor :name, :interval, :group, :valid_states, :initial_state
5     
6     attr_writer   :autostart
7     def autostart?; @autostart; end
8     
9     # api
10     attr_accessor :state, :behaviors, :metrics
11     
12     # internal
13     attr_accessor :mutex
14     
15     def initialize
16       @autostart ||= true
17       
18       # initial state is unmonitored
19       self.state = :unmonitored
20       
21       # the list of behaviors
22       self.behaviors = []
23       
24       # the list of conditions for each action
25       self.metrics = {nil => [], :unmonitored => []}
26       
27       # mutex
28       self.mutex = Mutex.new
29     end
30     
31     def prepare
32       self.valid_states.each do |state|
33         self.metrics[state] ||= []
34       end
35     end
36     
37     def valid?
38       valid = true
39       
40       # a name must be specified
41       if self.name.nil?
42         valid = false
43         LOG.log(self, :error, "No name was specified")
44       end
45       
46       # valid_states must be specified
47       if self.valid_states.nil?
48         valid = false
49         LOG.log(self, :error, "No valid_states array was specified")
50       end
51       
52       # valid_states must be specified
53       if self.initial_state.nil?
54         valid = false
55         LOG.log(self, :error, "No initial_state was specified")
56       end
57       
58       valid
59     end
60     
61     ###########################################################################
62     #
63     # Advanced mode
64     #
65     ###########################################################################
66     
67     def canonical_hash_form(to)
68       to.instance_of?(Symbol) ? {true => to} : to
69     end
70     
71     # Define a transition handler which consists of a set of conditions
72     def transition(start_states, end_states)
73       # convert end_states into canonical hash form
74       canonical_end_states = canonical_hash_form(end_states)
75       
76       Array(start_states).each do |start_state|
77         # validate start state
78         unless self.valid_states.include?(start_state)
79           abort "Invalid state :#{start_state}. Must be one of the symbols #{self.valid_states.map{|x| ":#{x}"}.join(', ')}"
80         end
81         
82         # create a new metric to hold the watch, end states, and conditions
83         m = Metric.new(self, canonical_end_states)
84         
85         if block_given?
86           # let the config file define some conditions on the metric
87           yield(m)
88         else
89           # add an :always condition if no block
90           m.condition(:always) do |c|
91             c.what = true
92           end
93         end
94         
95         # record the metric
96         self.metrics[start_state] ||= []
97         self.metrics[start_state] << m
98       end
99     end
100     
101     def lifecycle
102       # create a new metric to hold the watch and conditions
103       m = Metric.new(self)
104       
105       # let the config file define some conditions on the metric
106       yield(m)
107       
108       # record the metric
109       self.metrics[nil] << m
110     end
111     
112     ###########################################################################
113     #
114     # Lifecycle
115     #
116     ###########################################################################
117         
118     # Enable monitoring
119     def monitor
120       self.move(self.initial_state)
121     end
122     
123     # Disable monitoring
124     def unmonitor
125       self.move(:unmonitored)
126     end
127     
128     # Move from one state to another
129     def move(to_state)
130       orig_to_state = to_state
131       from_state = self.state
132       
133       msg = "#{self.name} move '#{from_state}' to '#{to_state}'"
134       Syslog.debug(msg)
135       LOG.log(self, :info, msg)
136       
137       # cleanup from current state
138       self.metrics[from_state].each { |m| m.disable }
139       
140       if to_state == :unmonitored
141         self.metrics[nil].each { |m| m.disable }
142       end
143       
144       # perform action
145       self.action(to_state)
146       
147       # enable simple mode
148       if [:start, :restart].include?(to_state) && self.metrics[to_state].empty?
149         to_state = :up
150       end
151       
152       # move to new state
153       self.metrics[to_state].each { |m| m.enable }
154       
155       # if no from state, enable lifecycle metric
156       if from_state == :unmonitored
157         self.metrics[nil].each { |m| m.enable }
158       end
159       
160       # set state
161       self.state = to_state
162       
163       # trigger
164       Trigger.broadcast(:state_change, [from_state, orig_to_state])
165       
166       # return self
167       self
168     end
169     
170     ###########################################################################
171     #
172     # Actions
173     #
174     ###########################################################################
175     
176     def method_missing(sym, *args)
177       unless (sym.to_s =~ /=$/)
178         super
179       end
180       
181       base = sym.to_s.chop.intern
182       
183       unless self.valid_states.include?(base)
184         super
185       end
186       
187       self.class.send(:attr_accessor, base)
188       self.send(sym, *args)
189     end
190     
191     #   +a+ is the action Symbol
192     #   +c+ is the Condition
193     def action(a, c = nil)
194       if self.respond_to?(a)
195         command = self.send(a)
196         
197         case command
198           when String
199             msg = "#{self.name} #{a}: #{command}"
200             Syslog.debug(msg)
201             LOG.log(self, :info, msg)
202             
203             system(command)
204           when Proc
205             msg = "#{self.name} #{a}: lambda"
206             Syslog.debug(msg)
207             LOG.log(self, :info, msg)
208             
209             command.call
210           else
211             raise NotImplementedError
212         end
213       end
214     end
215     
216     ###########################################################################
217     #
218     # Registration
219     #
220     ###########################################################################
221     
222     def register!
223       # override if necessary
224     end
225     
226     def unregister!
227       # override if necessary
228     end
229   end
230