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