ant_hill 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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