propogate netlink status to process exits condition
[god.git] / lib / god / task.rb
blob2f9205bd17f56c277d6c43c575f857d5ea0e3ec3
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 = Monitor.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         applog(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         applog(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         applog(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       self.mutex.synchronize do
131         orig_to_state = to_state
132         from_state = self.state
133         
134         msg = "#{self.name} move '#{from_state}' to '#{to_state}'"
135         applog(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(self, :state_change, [from_state, orig_to_state])
165         
166         # return self
167         self
168       end
169     end
170     
171     ###########################################################################
172     #
173     # Actions
174     #
175     ###########################################################################
176     
177     def method_missing(sym, *args)
178       unless (sym.to_s =~ /=$/)
179         super
180       end
181       
182       base = sym.to_s.chop.intern
183       
184       unless self.valid_states.include?(base)
185         super
186       end
187       
188       self.class.send(:attr_accessor, base)
189       self.send(sym, *args)
190     end
191     
192     #   +a+ is the action Symbol
193     #   +c+ is the Condition
194     def action(a, c = nil)
195       if self.respond_to?(a)
196         command = self.send(a)
197         
198         case command
199           when String
200             msg = "#{self.name} #{a}: #{command}"
201             applog(self, :info, msg)
202             
203             system(command)
204           when Proc
205             msg = "#{self.name} #{a}: lambda"
206             applog(self, :info, msg)
207             
208             command.call
209           else
210             raise NotImplementedError
211         end
212       end
213     end
214     
215     ###########################################################################
216     #
217     # Registration
218     #
219     ###########################################################################
220     
221     def register!
222       # override if necessary
223     end
224     
225     def unregister!
226       # override if necessary
227     end
228   end
229