finish lifecycle conditions handling and add flapper condition
[god.git] / lib / god / hub.rb
blob51b4e1298a9614177d8dfc9092e4ad3b65fe53b0
1 module God
2   
3   class Hub
4     class << self
5       # directory to hold conditions and their corresponding metric
6       # {condition => metric}
7       attr_accessor :directory
8     end
9     
10     self.directory = {}
11     
12     def self.attach(condition, metric)
13       self.directory[condition] = metric
14       
15       case condition
16         when PollCondition
17           Timer.get.schedule(condition, 0)
18         when EventCondition, TriggerCondition
19           condition.register
20       end
21     end
22     
23     def self.detach(condition)
24       self.directory.delete(condition)
25       
26       case condition
27         when PollCondition
28           Timer.get.unschedule(condition)
29         when EventCondition, TriggerCondition
30           condition.deregister
31       end
32     end
33     
34     def self.trigger(condition)
35       case condition
36         when PollCondition
37           self.handle_poll(condition)
38         when EventCondition, TriggerCondition
39           self.handle_event(condition)
40       end
41     end
42     
43     def self.handle_poll(condition)
44       Thread.new do
45         begin
46           metric = self.directory[condition]
47           
48           # it's possible that the timer will trigger an event before it can be cleared
49           # by an exiting metric, in which case it should be ignored
50           unless metric.nil?
51             watch = metric.watch
52             
53             watch.mutex.synchronize do
54               # run the test
55               result = condition.test
56               
57               # log
58               msg = watch.name + ' ' + condition.class.name + " [#{result}] " + self.dest_desc(metric, condition)
59               Syslog.debug(msg)
60               LOG.log(watch, :info, msg)
61               
62               # after-condition
63               condition.after
64               
65               # get the destination
66               dest = 
67               if result && condition.transition
68                 # condition override
69                 condition.transition
70               else
71                 # regular
72                 metric.destination && metric.destination[result]
73               end
74               
75               # transition or reschedule
76               if dest
77                 # transition
78                 begin
79                   watch.move(dest)
80                 rescue EventRegistrationFailedError
81                   msg = watch.name + ' Event registration failed, moving back to previous state'
82                   Syslog.debug(msg)
83                   LOG.log(watch, :info, msg)
84                   
85                   dest = watch.state
86                   retry
87                 end
88               else
89                 # reschedule
90                 Timer.get.schedule(condition)
91               end
92             end
93           end
94         rescue => e
95           message = format("Unhandled exception (%s): %s\n%s",
96                            e.class, e.message, e.backtrace.join("\n"))
97           Syslog.crit message
98           abort message
99         end
100       end
101     end
102     
103     def self.handle_event(condition)
104       Thread.new do
105         metric = self.directory[condition]
106         
107         unless metric.nil?
108           watch = metric.watch
109         
110           watch.mutex.synchronize do              
111             msg = watch.name + ' ' + condition.class.name + " [true] " + self.dest_desc(metric, condition)
112             Syslog.debug(msg)
113             LOG.log(watch, :info, msg)
114           
115             # get the destination
116             dest = 
117             if condition.transition
118               # condition override
119               condition.transition
120             else
121               # regular
122               metric.destination && metric.destination[true]
123             end
124             
125             if dest
126               watch.move(dest)
127             end
128           end
129         end
130       end
131     end
132     
133     # helpers
134   
135     def self.dest_desc(metric, condition)
136       if metric.destination
137         metric.destination.inspect
138       else
139         if condition.transition
140           {true => condition.transition}.inspect
141         else
142           'none'
143         end
144       end
145     end
146   end
147