ant_hill 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,150 @@
1
+ module AntHill
2
+
3
+ # Base class for setup/run logic
4
+ # Childrens should implement specific logic for particular ant type
5
+ class CreepModifier
6
+
7
+ # Creep object
8
+ attr_reader :creep
9
+
10
+ include DRbUndumped
11
+
12
+ # Initialize
13
+ # +creep+:: Creep which we gonna change
14
+ def initialize(creep)
15
+ @creep = creep
16
+ end
17
+
18
+ # Find diff between current params of creep and ant params
19
+ def find_diff(ant)
20
+ diff = {}
21
+ ant_config = ant.params
22
+ creep_config = creep.current_params
23
+
24
+ ant_config.each_key{|k|
25
+ diff[k] = ant_config[k] unless ant_config[k] == creep_config[k]
26
+ }
27
+ diff
28
+ end
29
+
30
+ # Run ant
31
+ # +ant+:: ant to run
32
+ def run_ant(ant)
33
+ begin
34
+ before_run(ant)
35
+ rescue => e
36
+ logger.error "Error during run ant_started method: #{e} \n #{e.backtrace}"
37
+ end
38
+ begin
39
+ out = run(ant)
40
+ ant.output = out
41
+ rescue => e
42
+ logger.error "Error during processing run method: #{e} \n #{e.backtrace}"
43
+ ensure
44
+ begin
45
+ after_run(ant)
46
+ rescue => e
47
+ logger.error "Error in ant_finished method: #{e} \n #{e.backtrace}"
48
+ end
49
+ end
50
+ end
51
+
52
+ # Setup ant for running
53
+ # +ant+:: Ant to setup
54
+ def setup_ant(ant)
55
+ ant.runner = creep
56
+ ant.change_status(:setup)
57
+ begin
58
+ before_setup(ant)
59
+ rescue => e
60
+ logger.error "Error during processing before_setup method: #{e} \n #{e.backtrace}"
61
+ end
62
+ result = nil
63
+ begin
64
+ result = setup(ant)
65
+ rescue => e
66
+ logger.error "Error during processing setup method: #{e} \n #{e.backtrace}"
67
+ end
68
+ begin
69
+ after_setup(ant)
70
+ rescue => e
71
+ logger.error "Error during processing after_setup method: #{e} \n #{e.backtrace}"
72
+ end
73
+ result
74
+ end
75
+
76
+ # Logger for creep
77
+ def logger
78
+ creep.logger
79
+ end
80
+
81
+ # Return calculated setup time for ant
82
+ # +ant+:: ant to setup
83
+ # Can be redefined in child class
84
+ def get_setup_time(ant)
85
+ end
86
+
87
+ # Return calculated run time for ant
88
+ # +ant+:: ant to run
89
+ # Can be redefined in child class
90
+ def get_run_time(ant)
91
+ end
92
+
93
+ # Before run hook
94
+ # +ant+:: ant to run
95
+ def before_run(ant)
96
+ end
97
+
98
+ # After run hook
99
+ # +ant+:: ant was ran
100
+ def after_run(ant)
101
+ end
102
+
103
+ # Before setup hook
104
+ # +ant+:: ant to setup
105
+ def before_setup(ant)
106
+ end
107
+
108
+ # After setup hook
109
+ # +ant+:: ant was set up
110
+ def after_setup(ant)
111
+ end
112
+
113
+ # Setup failed hook
114
+ # +ant+:: ant for wich setup failed
115
+ def setup_failed(ant)
116
+ end
117
+
118
+ # Before process hook
119
+ # +ant+:: ant to be processed
120
+ def before_process(ant)
121
+ end
122
+
123
+ # After process hook
124
+ # +ant+:: ant was processed
125
+ def after_process(ant)
126
+ end
127
+
128
+ # Setup implementation
129
+ # +ant+:: ant to set up
130
+ def setup(ant)
131
+ true
132
+ end
133
+
134
+ # Run implementation
135
+ # +ant+:: ant to run
136
+ def run(ant)
137
+ end
138
+
139
+ # Check node is set up
140
+ # +ant+:: ant to check
141
+ def check(ant)
142
+ true
143
+ end
144
+
145
+ # Params from ant to be coppied after setup
146
+ def creep_params
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,35 @@
1
+ module AntHill
2
+ # Log class
3
+ class Log
4
+ include DRbUndumped
5
+ # Singleton class
6
+ class << self
7
+ @@loggers = {}
8
+ # Return logger for specified name
9
+ def logger_for(name, config = Configuration.config)
10
+ return @@loggers[name] if @@loggers[name]
11
+ verbose = config.log_level
12
+ path = config.log_dir
13
+ FileUtils.mkdir_p path unless File.exists?(path)
14
+ logger = Logger.new(path+"/#{name}.log", (config.log_num_old_files) || 5, (config.log_size || 50*1024*1024) )
15
+ logger.level = level(config.log_level)
16
+ logger.formatter = Logger::Formatter.new
17
+ @@loggers[name] = logger
18
+ end
19
+ private
20
+ # Convert symbol log level to Logger log level
21
+ #
22
+ def level(level)
23
+ case level
24
+ when :fatal then Logger::FATAL
25
+ when :error then Logger::ERROR
26
+ when :warn then Logger::WARN
27
+ when :info then Logger::INFO
28
+ when :debug then Logger::DEBUG
29
+ else Logger::ERROR
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,334 @@
1
+ module AntHill
2
+ # Main class that rule all the kingdom
3
+ class Queen
4
+ # +creeps+:: list of creeps
5
+ # +ants+:: list of ants
6
+ # +colonies+:: list of colonies
7
+ attr_reader :creeps, :ants, :colonies
8
+
9
+ # Default host for DRb
10
+ DRB_HOST = '127.0.0.1'
11
+ # Default port for DRb
12
+ DRB_PORT = 6666
13
+
14
+ include DRbUndumped
15
+
16
+ # Initialize
17
+ # +config+:: Configuration object
18
+ def initialize(config = Configuration.config)
19
+ @config = config
20
+ @ants = []
21
+ @colony_queue = []
22
+ @colonies = []
23
+ @drb_host = config.drb_host
24
+ @drb_port = config.drb_port
25
+ @@mutex = Mutex.new
26
+ trap("INT") do
27
+ puts "Terminating... Pls wait"
28
+ @creeps.each{|c| c.kill_connections }
29
+ @threads.each{|th| th.exit }
30
+ end
31
+ @active = true
32
+ @loaded_params = {}
33
+ end
34
+
35
+ # Return ants size
36
+ def size
37
+ @ants.size
38
+ end
39
+
40
+ # Service method
41
+ def service
42
+ @threads = []
43
+ spawn_creeps(@config.creeps)
44
+ spawn_drb_queen
45
+ spawn_colonies_processor
46
+ at_exit do
47
+ save_queen(@config.queen_filename || "queen.yml")
48
+ end
49
+ @threads.each{|t| t.join}
50
+ rescue => e
51
+ logger.error "There was an error in queen. Details: #{e}\n#{e.backtrace.join("\n")}"
52
+ end
53
+
54
+ # Create colony
55
+ # +params+:: params for colony
56
+ # +loaded_params+:: loaded params for respawning queen
57
+ def create_colony(params={}, loaded_params = nil)
58
+ type = params['type']
59
+ type = loaded_params[:params]['type'] if loaded_params
60
+ colony_class = @config.ant_colony_class(type)
61
+ if colony_class
62
+ colony = colony_class.new(params)
63
+ colony.from_hash(loaded_params) if loaded_params
64
+ @colony_queue << colony unless loaded_params
65
+ @colonies << colony
66
+ else
67
+ logger.error "Couldn't process request #{params} because of previous errors"
68
+ end
69
+ colony
70
+ end
71
+
72
+ # Initialize and start threads for each creep
73
+ # +creeps+: array of hashes with creep params
74
+ # Example: [{'name' => 'creep1', 'host'=> 'hostname', 'user' => 'login_user', 'password' => 'user_password'}]
75
+ def spawn_creeps(creeps)
76
+ @creeps = []
77
+ loaded_params = @loaded_params[:creeps]
78
+ creeps.each do |creep_config|
79
+ creep_loaded = loaded_params && loaded_params.find{|cr| cr[:hill_cfg]['name'] == creep_config['name'] } || {}
80
+ add_creep(creep_config, creep_loaded)
81
+ end
82
+ end
83
+
84
+ # Adding new creep and creatinf thread for it
85
+ # +creep_config+:: hash of params for creep
86
+ # Example: {'name' => 'creep1', 'host'=> 'hostname', 'user' => 'login_user', 'password' => 'user_password'}
87
+ # +creep_loaded+:: creep loaded from saved file
88
+ def add_creep(creep_config, creep_loaded={})
89
+ @threads << Thread.new{
90
+ c = Creep.new
91
+ c.configure(creep_config)
92
+ c.from_hash(creep_loaded)
93
+ @creeps << c
94
+ Thread.current["name"]=c.to_s
95
+ c.service
96
+ }
97
+ end
98
+
99
+ # Delete creep
100
+ # +creep_name+:: creep name to delete
101
+ # +graceful+:: default true, if true finish processing before delete
102
+ def delete_creep(creep_name, graceful=true)
103
+ creep = @creeps.find{|c| c.name.to_s =~ /#{creep_name}/}
104
+ thread = @threads.find{|t| t['name'] == creep.to_s} if creep
105
+ if graceful
106
+ creep.disable!
107
+ while creep.busy? do
108
+ sleep 1
109
+ end
110
+ end
111
+ thread.terminate if thread
112
+ @threads.delete(thread) if thread
113
+ @creeps.delete(creep) if creep
114
+ end
115
+
116
+ # Create drb interface for queen
117
+ def spawn_drb_queen
118
+ @threads << Thread.new{
119
+ begin
120
+ Thread.current["name"]="main"
121
+ DRb.start_service "druby://#{@drb_host || DRB_HOST}:#{@drb_port || DRB_PORT}", self
122
+ DRb.thread.join
123
+ rescue => e
124
+ logger.error "There was an error in drb_queen =(. Details: #{e}\n#{e.backtrace}"
125
+ end
126
+ }
127
+ end
128
+
129
+ # Create thread for processing new colonies
130
+ def spawn_colonies_processor
131
+ @threads << Thread.new{
132
+ Thread.current["name"]="colony queue processor"
133
+ while true do
134
+ if @active
135
+ @colony_processor_busy = true
136
+ colony = @colony_queue.shift
137
+ if colony
138
+ new_ants = colony.get_ants
139
+ add_ants(new_ants)
140
+ end
141
+ @colony_processor_busy = false
142
+ end
143
+ sleep 1
144
+ end
145
+ }
146
+ end
147
+
148
+ # Add new ants to queue
149
+ def add_ants(ants)
150
+ @ants += ants
151
+ end
152
+
153
+ # Find ant for creep
154
+ # +creep+:: creep to find ant
155
+ def find_ant(creep)
156
+ return nil if @ants.empty?
157
+ winner = nil
158
+ @@mutex.synchronize{
159
+ winner = max_priority_ant(creep)
160
+ @ants.delete(winner) if winner
161
+ }
162
+ winner
163
+ end
164
+
165
+ # Return ant with max priority for creep
166
+ # +creep+:: creep object
167
+ def max_priority_ant(creep)
168
+ max_ant = nil
169
+ max_priority =-Float::INFINITY
170
+ @ants.each do |a|
171
+ next if a.prior < max_priority
172
+ if (prior=a.priority_cache(creep)) > max_priority
173
+ max_priority = prior
174
+ max_ant = a
175
+ end
176
+ end
177
+ max_ant
178
+ rescue NoFreeConnectionError => e
179
+ logger.error "Couldn't find any free connection for creep #{creep}. #{e}: #{e.backtrace.join("\n")}"
180
+ creep.disable!
181
+ nil
182
+ end
183
+
184
+ # Reset priority for specified creep for all ants
185
+ def reset_priority_for_creep(creep)
186
+ @ants.each{|a| a.delete_cache_for_creep(creep)}
187
+ end
188
+
189
+ # Return logger for queen
190
+ def logger
191
+ Log.logger_for(:queen)
192
+ end
193
+
194
+ # Suspend all processing and wait while it's done
195
+ def suspend
196
+ @active = false
197
+ while creeps.any?{|creep| creep.busy? }
198
+ sleep 1
199
+ end
200
+ while @colony_processor_busy
201
+ sleep 1
202
+ end
203
+ end
204
+
205
+ # Release all processing
206
+ def release
207
+ @active = true
208
+ end
209
+
210
+ # Find colonies for params
211
+ # +params+:: hash of params to match colony
212
+ def find_colonies(params)
213
+ @colonies.select do |colony|
214
+ colony.is_it_me?(params)
215
+ end
216
+ end
217
+
218
+ # Kill colonies matching params
219
+ # +params+:: hash of params
220
+ def kill_colony(params)
221
+ if params.is_a?(AntColony)
222
+ to_kill = [ params ]
223
+ else
224
+ to_kill = find_colonies(params)
225
+ end
226
+ @@mutex.synchronize{
227
+ to_kill.each do |colony|
228
+ colony.kill
229
+ @ants.reject!{|ant|
230
+ ant.colony == colony
231
+ }
232
+ @colonies.delete(colony)
233
+ end
234
+ }
235
+ end
236
+
237
+ # Save queen to file
238
+ # +filename+:: filename to store queen data
239
+ def save_queen(filename)
240
+ queen_hash = to_hash
241
+ File.open(filename, "w+") { |f| f.puts queen_hash.to_yaml}
242
+ end
243
+
244
+ # Restore queen from file
245
+ # +filename+:: filenme with queen data
246
+ def restore_queen(filename)
247
+ hash = YAML::load_file(filename)
248
+ @loaded_params = hash
249
+ from_hash(hash)
250
+ end
251
+
252
+ # Initialize queen from loaded hash
253
+ # +hash+:: queen hash
254
+ def from_hash(hash)
255
+ colonies = hash[:colonies]
256
+ tmp = {}
257
+ @config.from_hash(hash[:configuration])
258
+ colonies.each do |col|
259
+ colony = create_colony({},col)
260
+ tmp[col[:id]] = colony
261
+ end
262
+ @colonies.each{|c| add_ants(c.ants)}
263
+ @colony_queue = hash[:colony_queue].collect{|cq| tmp[cq]}
264
+ end
265
+
266
+ # Convert queen to hash
267
+ # +include_finished+:: should finished colonies and ants be includes to hash?
268
+ def to_hash(include_finished = false)
269
+ {
270
+ :colonies => @colonies.collect{|ac| ac.to_hash(include_finished) },
271
+ :colony_queue => @colony_queue.collect{|ac| ac.object_id },
272
+ :creeps => @creeps.collect{|c| c.to_hash },
273
+ :configuration => @config.to_hash
274
+ }
275
+ end
276
+
277
+ # Check if queen is active
278
+ def active?; @active; end
279
+
280
+ # Singleton object
281
+ class << self
282
+ # Check if mutex is locked
283
+ def locked?
284
+ @@mutex.locked?
285
+ end
286
+
287
+ # Return or create current queen
288
+ def queen
289
+ @@queen ||= self.new
290
+ end
291
+
292
+ # Connect to DRb interface of queen
293
+ # +host+:: host where DRb is started
294
+ def drb_queen(host = 'localhost')
295
+ DRb.start_service
296
+ queen = DRbObject.new_with_uri "druby://#{host}:6666"
297
+ rescue => e
298
+ puts e
299
+ end
300
+
301
+ # Creates colony for arguments
302
+ # +args+:: command line arguments
303
+ # +host+:: DRb queen host
304
+ def create_colony(args, host = 'localhost')
305
+ drb_queen(host).create_colony parse_args(args)
306
+ end
307
+
308
+ # Return list of creeps
309
+ # +host+:: DRb queen host
310
+ def creeps(host = 'localhost')
311
+ drb_queen(host).creeps
312
+ end
313
+
314
+ # Kill colony(colonies) matching arguments
315
+ # +args+:: command line arguments
316
+ # +host+:: DRb queen host
317
+ def kill_colony(args, host = 'localhost')
318
+ drb_queen(host).kill_colony parse_args(args)
319
+ end
320
+
321
+ private
322
+ # Convert command line arguments to hash
323
+ # +args+:: command line arguments
324
+ def parse_args(args)
325
+ result = {}
326
+ args.each do |arg|
327
+ pair = arg.split("=")
328
+ result[pair[0]]=pair[1]
329
+ end
330
+ result
331
+ end
332
+ end
333
+ end
334
+ end