bookie_accounting 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Rakefile +4 -7
- data/TODO +1 -0
- data/bin/bookie-create-tables +1 -1
- data/bin/bookie-send +10 -1
- data/bookie_accounting.gemspec +6 -3
- data/lib/bookie/database.rb +241 -175
- data/lib/bookie/extensions.rb +72 -0
- data/lib/bookie/formatter.rb +15 -7
- data/lib/bookie/formatters/comma_dump.rb +9 -3
- data/lib/bookie/formatters/stdout.rb +1 -1
- data/lib/bookie/sender.rb +76 -16
- data/lib/bookie/senders/standalone.rb +2 -2
- data/lib/bookie/version.rb +1 -1
- data/rpm/bookie_accounting.spec.erb +2 -0
- data/spec/comma_dump_formatter_spec.rb +21 -12
- data/spec/database_spec.rb +199 -103
- data/spec/extensions_spec.rb +25 -0
- data/spec/formatter_spec.rb +37 -32
- data/spec/sender_spec.rb +127 -68
- data/spec/spec_helper.rb +15 -1
- data/spec/spreadsheet_formatter_spec.rb +16 -14
- data/spec/stdout_formatter_spec.rb +10 -8
- metadata +57 -11
data/lib/bookie/database.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'bookie/config'
|
2
|
+
require 'bookie/extensions'
|
2
3
|
|
3
4
|
require 'active_record'
|
4
5
|
|
5
6
|
module Bookie
|
6
|
-
|
7
|
-
#
|
8
|
-
#For a list of fields in the various models, see {Database Tables}[link:rdoc/database_rdoc.html]
|
7
|
+
##
|
8
|
+
#Contains database-related code and models
|
9
9
|
module Database
|
10
10
|
|
11
11
|
##
|
@@ -57,6 +57,8 @@ module Bookie
|
|
57
57
|
def end_time
|
58
58
|
return start_time + wall_time
|
59
59
|
end
|
60
|
+
|
61
|
+
#To consider: disable #end_time= ?
|
60
62
|
|
61
63
|
def self.by_user(user)
|
62
64
|
where('jobs.user_id = ?', user.id)
|
@@ -83,7 +85,7 @@ module Bookie
|
|
83
85
|
def self.by_group_name(group_name)
|
84
86
|
group = Group.find_by_name(group_name)
|
85
87
|
return joins(:user).where('users.group_id = ?', group.id) if group
|
86
|
-
|
88
|
+
where('1=0')
|
87
89
|
end
|
88
90
|
|
89
91
|
##
|
@@ -113,7 +115,13 @@ module Bookie
|
|
113
115
|
##
|
114
116
|
#Finds all jobs whose running intervals overlap the given time range
|
115
117
|
def self.by_time_range_inclusive(time_range)
|
116
|
-
|
118
|
+
if time_range.empty?
|
119
|
+
where('1=0')
|
120
|
+
elsif time_range.exclude_end?
|
121
|
+
where('? <= jobs.end_time AND jobs.start_time < ?', time_range.first, time_range.last)
|
122
|
+
else
|
123
|
+
where('? <= jobs.end_time AND jobs.start_time <= ?', time_range.first, time_range.last)
|
124
|
+
end
|
117
125
|
end
|
118
126
|
|
119
127
|
##
|
@@ -125,12 +133,14 @@ module Bookie
|
|
125
133
|
#- <tt>:memory_time</tt>: the sum of memory * wall_time for all jobs in the interval
|
126
134
|
#- <tt>:successful</tt>: the number of jobs that have completed successfully
|
127
135
|
#
|
128
|
-
#This method should probably not be
|
136
|
+
#This method should probably not be chained with other queries that filter by start/end time.
|
137
|
+
#
|
138
|
+
#To consider: filter out jobs with 0 CPU time?
|
129
139
|
def self.summary(time_range = nil)
|
130
140
|
time_range = time_range.normalized if time_range
|
131
141
|
jobs = self
|
132
142
|
jobs = jobs.by_time_range_inclusive(time_range) if time_range
|
133
|
-
jobs = jobs.
|
143
|
+
jobs = jobs.all_with_relations
|
134
144
|
cpu_time = 0
|
135
145
|
successful_jobs = 0
|
136
146
|
memory_time = 0
|
@@ -151,10 +161,7 @@ module Bookie
|
|
151
161
|
#To consider: what should I do about jobs that only report a max memory value?
|
152
162
|
memory_time += job.memory * clipped_wall_time
|
153
163
|
end
|
154
|
-
|
155
|
-
if job.exit_code == 0 && (!time_range || job.end_time < time_range.end) then
|
156
|
-
successful_jobs += 1
|
157
|
-
end
|
164
|
+
successful_jobs += 1 if job.exit_code == 0
|
158
165
|
end
|
159
166
|
|
160
167
|
return {
|
@@ -171,40 +178,38 @@ module Bookie
|
|
171
178
|
#Relations are not cached between calls.
|
172
179
|
def self.all_with_relations
|
173
180
|
jobs = all
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
groups[group.id] = group
|
207
|
-
end
|
181
|
+
users = {}
|
182
|
+
groups = {}
|
183
|
+
systems = {}
|
184
|
+
system_types = {}
|
185
|
+
jobs.each do |job|
|
186
|
+
system = systems[job.system_id]
|
187
|
+
if system
|
188
|
+
job.system = system
|
189
|
+
else
|
190
|
+
system = job.system
|
191
|
+
systems[system.id] = system
|
192
|
+
end
|
193
|
+
system_type = system_types[system.system_type_id]
|
194
|
+
if system_type
|
195
|
+
system.system_type = system_type
|
196
|
+
else
|
197
|
+
system_type = system.system_type
|
198
|
+
system_types[system_type.id] = system_type
|
199
|
+
end
|
200
|
+
user = users[job.user_id]
|
201
|
+
if user
|
202
|
+
job.user = user
|
203
|
+
else
|
204
|
+
user = job.user
|
205
|
+
users[user.id] = user
|
206
|
+
end
|
207
|
+
group = groups[user.group_id]
|
208
|
+
if group
|
209
|
+
user.group = group
|
210
|
+
else
|
211
|
+
group = user.group
|
212
|
+
groups[group.id] = group
|
208
213
|
end
|
209
214
|
end
|
210
215
|
|
@@ -231,50 +236,89 @@ module Bookie
|
|
231
236
|
end
|
232
237
|
end
|
233
238
|
|
239
|
+
##
|
240
|
+
#A cached summary of Jobs in the database
|
241
|
+
#
|
242
|
+
#Most summary operations should be performed through this class to improve efficiency.
|
234
243
|
class JobSummary < ActiveRecord::Base
|
235
244
|
self.table_name = :job_summaries
|
236
245
|
|
237
246
|
belongs_to :user
|
238
247
|
belongs_to :system
|
248
|
+
|
249
|
+
attr_accessible :date, :user, :user_id, :system, :system_id, :command_name, :cpu_time, :memory_time
|
239
250
|
|
251
|
+
##
|
252
|
+
#Filters by date
|
240
253
|
def self.by_date(date)
|
241
254
|
where('job_summaries.date = ?', date)
|
242
255
|
end
|
256
|
+
|
257
|
+
##
|
258
|
+
#Filters by a date range
|
259
|
+
def self.by_date_range(range)
|
260
|
+
range = range.normalized
|
261
|
+
if range.exclude_end?
|
262
|
+
where('? <= job_summaries.date AND job_summaries.date < ?', range.begin, range.end)
|
263
|
+
else
|
264
|
+
where('? <= job_summaries.date AND job_summaries.date <= ?', range.begin, range.end)
|
265
|
+
end
|
266
|
+
end
|
243
267
|
|
268
|
+
##
|
269
|
+
#Filters by user
|
244
270
|
def self.by_user(user)
|
245
271
|
where('job_summaries.user_id = ?', user.id)
|
246
272
|
end
|
247
273
|
|
274
|
+
##
|
275
|
+
#Filters by user name
|
248
276
|
def self.by_user_name(name)
|
249
277
|
joins(:user).where('users.name = ?', name)
|
250
278
|
end
|
251
279
|
|
280
|
+
##
|
281
|
+
#Filters by group
|
252
282
|
def self.by_group(group)
|
253
283
|
joins(:user).where('users.group_id = ?', group.id)
|
254
284
|
end
|
255
285
|
|
286
|
+
##
|
287
|
+
#Filters by group name
|
256
288
|
def self.by_group_name(name)
|
257
289
|
group = Group.find_by_name(name)
|
258
290
|
return by_group(group) if group
|
259
|
-
|
291
|
+
where('1=0')
|
260
292
|
end
|
261
293
|
|
294
|
+
##
|
295
|
+
#Filters by system
|
262
296
|
def self.by_system(system)
|
263
297
|
where('job_summaries.system_id = ?', system.id)
|
264
298
|
end
|
265
299
|
|
300
|
+
##
|
301
|
+
#Filters by system name
|
266
302
|
def self.by_system_name(name)
|
267
303
|
joins(:system).where('systems.name = ?', name)
|
268
304
|
end
|
269
305
|
|
306
|
+
##
|
307
|
+
#Filters by system type
|
270
308
|
def self.by_system_type(type)
|
271
309
|
joins(:system).where('systems.system_type_id = ?', type.id)
|
272
310
|
end
|
273
311
|
|
312
|
+
##
|
313
|
+
#Filters by command name
|
274
314
|
def self.by_command_name(cmd)
|
275
315
|
where('job_summaries.command_name = ?', cmd)
|
276
316
|
end
|
277
317
|
|
318
|
+
##
|
319
|
+
#Attempts to find a JobSummary with the given date, user_id, system_id, and command_name
|
320
|
+
#
|
321
|
+
#If one does not exist, a new JobSummary will be instantiated (but not saved to the database).
|
278
322
|
def self.find_or_new(date, user_id, system_id, command_name)
|
279
323
|
str = by_date(date).where(:user_id => user_id, :system_id => system_id).by_command_name(command_name).to_sql
|
280
324
|
summary = by_date(date).where(:user_id => user_id, :system_id => system_id).by_command_name(command_name).first
|
@@ -287,27 +331,62 @@ module Bookie
|
|
287
331
|
summary
|
288
332
|
end
|
289
333
|
|
334
|
+
##
|
335
|
+
#Create cached summaries for the given date
|
336
|
+
#
|
337
|
+
#The date is interpreted as being in UTC.
|
338
|
+
#
|
339
|
+
#If there is nothing to summarize, a dummy summary will be created.
|
340
|
+
#
|
341
|
+
#Uses Lock::synchronize internally; should not be used in transaction blocks
|
290
342
|
def self.summarize(date)
|
291
343
|
jobs = Job
|
292
344
|
unscoped = self.unscoped
|
293
|
-
|
345
|
+
time_min = date.to_utc_time
|
346
|
+
time_range = time_min ... time_min + 1.days
|
294
347
|
day_jobs = jobs.by_time_range_inclusive(time_range)
|
348
|
+
|
349
|
+
#Find the sets of unique values.
|
295
350
|
value_sets = day_jobs.select('user_id, system_id, command_name').uniq
|
296
|
-
value_sets.
|
297
|
-
|
298
|
-
|
351
|
+
if value_sets.empty?
|
352
|
+
user = User.select(:id).first
|
353
|
+
system = System.select(:id).first
|
354
|
+
#If there are no users or no systems, we can't create the dummy summary, so just return.
|
355
|
+
return unless user && system
|
356
|
+
#Create a dummy summary so summary() doesn't keep trying to create one.
|
299
357
|
Lock[:job_summaries].synchronize do
|
300
|
-
sum = unscoped.find_or_new(date,
|
301
|
-
|
302
|
-
sum.
|
303
|
-
sum.cpu_time = summary[:cpu_time]
|
304
|
-
sum.memory_time = summary[:memory_time]
|
305
|
-
sum.successful = summary[:successful]
|
358
|
+
sum = unscoped.find_or_new(date, user.id, system.id, '')
|
359
|
+
sum.cpu_time = 0
|
360
|
+
sum.memory_time = 0
|
306
361
|
sum.save!
|
307
362
|
end
|
363
|
+
else
|
364
|
+
value_sets.each do |set|
|
365
|
+
summary_jobs = jobs.where(:user_id => set.user_id).where(:system_id => set.system_id).by_command_name(set.command_name)
|
366
|
+
summary = summary_jobs.summary(time_range)
|
367
|
+
Lock[:job_summaries].synchronize do
|
368
|
+
sum = unscoped.find_or_new(date, set.user_id, set.system_id, set.command_name)
|
369
|
+
sum.cpu_time = summary[:cpu_time]
|
370
|
+
sum.memory_time = summary[:memory_time]
|
371
|
+
sum.save!
|
372
|
+
end
|
373
|
+
end
|
308
374
|
end
|
309
375
|
end
|
310
376
|
|
377
|
+
##
|
378
|
+
#Returns a summary of jobs in the database
|
379
|
+
#
|
380
|
+
#The following options are supported:
|
381
|
+
#- [<tt>:range</tt>] restricts the summary to a specific time interval (specified as a Range of Time objects)
|
382
|
+
#- [<tt>:jobs</tt>] the jobs on which the summary should operate
|
383
|
+
#
|
384
|
+
#Internally, this may call JobSummary::summary, which uses Lock#synchronize, so this should not be used inside a transaction block.
|
385
|
+
#
|
386
|
+
#When filtering, the same filters must be applied to both the Jobs and the JobSummaries. For example:
|
387
|
+
# jobs = Bookie::Database::Job.by_user_name('root')
|
388
|
+
# summaries = Bookie::Database::Job.by_user_name('root')
|
389
|
+
# puts summaries.summary(:jobs => jobs)
|
311
390
|
def self.summary(opts = {})
|
312
391
|
jobs = opts[:jobs] || Job
|
313
392
|
range = opts[:range]
|
@@ -323,7 +402,7 @@ module Bookie
|
|
323
402
|
first_started_system = System.order(:start_time).first
|
324
403
|
range = first_started_system.start_time ... end_time
|
325
404
|
else
|
326
|
-
range =
|
405
|
+
range = Time.new ... Time.new
|
327
406
|
end
|
328
407
|
end
|
329
408
|
range = range.normalized
|
@@ -333,54 +412,66 @@ module Bookie
|
|
333
412
|
memory_time = 0
|
334
413
|
successful = 0
|
335
414
|
|
336
|
-
#
|
337
|
-
|
338
|
-
unless
|
339
|
-
date_begin
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
415
|
+
#Is the beginning somewhere between days?
|
416
|
+
date_begin = range.begin.utc.to_date
|
417
|
+
unless date_begin.to_utc_time == range.begin
|
418
|
+
date_begin += 1
|
419
|
+
time_before_max = [date_begin.to_utc_time, range.end].min
|
420
|
+
time_before_min = range.begin
|
421
|
+
summary = jobs.summary(time_before_min ... time_before_max)
|
422
|
+
cpu_time += summary[:cpu_time]
|
423
|
+
memory_time += summary[:memory_time]
|
424
|
+
end
|
425
|
+
|
426
|
+
#Is the end somewhere between days?
|
427
|
+
date_end = range.end.utc.to_date
|
428
|
+
time_after_min = date_end.to_utc_time
|
429
|
+
unless time_after_min <= range.begin
|
430
|
+
time_after_max = range.end
|
431
|
+
time_after_range = Range.new(time_after_min, time_after_max, range.exclude_end?)
|
432
|
+
unless time_after_range.empty?
|
433
|
+
summary = jobs.summary(time_after_range)
|
345
434
|
cpu_time += summary[:cpu_time]
|
346
435
|
memory_time += summary[:memory_time]
|
347
|
-
successful += summary[:successful]
|
348
436
|
end
|
349
|
-
|
350
|
-
date_end = range.end.to_date
|
351
|
-
time_after_min = date_end.to_time
|
352
|
-
unless time_after_min <= range.begin
|
353
|
-
time_after_max = range.end
|
354
|
-
time_after_range = Range.new(time_after_min, time_after_max, range.exclude_end?)
|
355
|
-
unless time_after_range.empty?
|
356
|
-
summary = jobs.summary(time_after_range)
|
357
|
-
cpu_time += summary[:cpu_time]
|
358
|
-
memory_time += summary[:memory_time]
|
359
|
-
successful += summary[:successful]
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
date_range = date_begin ... date_end
|
364
437
|
end
|
365
438
|
|
439
|
+
date_range = date_begin ... date_end
|
440
|
+
|
366
441
|
unscoped = self.unscoped
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
cpu_time +=
|
374
|
-
memory_time +=
|
375
|
-
|
376
|
-
|
377
|
-
|
442
|
+
summaries = by_date_range(date_range).order(:date).all
|
443
|
+
index = 0
|
444
|
+
date_range.each do |date|
|
445
|
+
new_index = index
|
446
|
+
sum = summaries[new_index]
|
447
|
+
while sum && sum.date == date do
|
448
|
+
cpu_time += sum.cpu_time
|
449
|
+
memory_time += sum.memory_time
|
450
|
+
new_index += 1
|
451
|
+
sum = summaries[new_index]
|
452
|
+
end
|
453
|
+
#Did we actually process any summaries?
|
454
|
+
if new_index == index
|
455
|
+
#Nope. Create the summaries.
|
456
|
+
#To consider: optimize out the query?
|
457
|
+
unscoped.summarize(date)
|
458
|
+
sums = by_date(date)
|
459
|
+
sums.each do |sum|
|
460
|
+
cpu_time += sum.cpu_time
|
461
|
+
memory_time += sum.memory_time
|
462
|
+
end
|
463
|
+
end
|
378
464
|
end
|
379
465
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
466
|
+
if range && range.empty?
|
467
|
+
num_jobs = 0
|
468
|
+
successful = 0
|
469
|
+
else
|
470
|
+
jobs = jobs.by_time_range_inclusive(range)
|
471
|
+
num_jobs = jobs.count
|
472
|
+
successful = jobs.where('jobs.exit_code = 0').count
|
473
|
+
end
|
474
|
+
|
384
475
|
{
|
385
476
|
:num_jobs => num_jobs,
|
386
477
|
:cpu_time => cpu_time,
|
@@ -389,19 +480,19 @@ module Bookie
|
|
389
480
|
}
|
390
481
|
end
|
391
482
|
|
392
|
-
validates_presence_of :user_id, :system_id, :date, :
|
483
|
+
validates_presence_of :user_id, :system_id, :date, :cpu_time, :memory_time
|
393
484
|
|
394
485
|
validates_each :command_name do |record, attr, value|
|
395
486
|
record.errors.add(attr, 'must not be nil') if value == nil
|
396
487
|
end
|
397
488
|
|
398
|
-
validates_each :
|
489
|
+
validates_each :cpu_time, :memory_time do |record, attr, value|
|
399
490
|
record.errors.add(attr, 'must be a non-negative integer') unless value && value >= 0
|
400
491
|
end
|
401
492
|
end
|
402
493
|
|
403
494
|
##
|
404
|
-
#A group
|
495
|
+
#A group of users
|
405
496
|
class Group < ActiveRecord::Base
|
406
497
|
has_many :users
|
407
498
|
|
@@ -488,13 +579,26 @@ module Bookie
|
|
488
579
|
where('systems.system_type_id = ?', sys_type.id)
|
489
580
|
end
|
490
581
|
|
582
|
+
##
|
583
|
+
#Finds all systems whose running intervals overlap the given time range
|
584
|
+
#
|
585
|
+
#To do: unit test.
|
586
|
+
def self.by_time_range_inclusive(time_range)
|
587
|
+
if time_range.empty?
|
588
|
+
where('1=0')
|
589
|
+
elsif time_range.exclude_end?
|
590
|
+
where('(? <= systems.end_time OR systems.end_time IS NULL) AND systems.start_time < ?', time_range.first, time_range.last)
|
591
|
+
else
|
592
|
+
where('(? <= systems.end_time OR systems.end_time IS NULL) AND systems.start_time <= ?', time_range.first, time_range.last)
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
491
596
|
##
|
492
597
|
#Finds the current system for a given sender and time
|
493
598
|
#
|
494
599
|
#This method also checks that this system's specifications are the same as those in the database and raises an error if they are different.
|
495
600
|
#
|
496
601
|
#This uses Lock#synchronize internally, so it probably should not be called within a transaction block.
|
497
|
-
#
|
498
602
|
def self.find_current(sender, time = nil)
|
499
603
|
time ||= Time.now
|
500
604
|
config = sender.config
|
@@ -516,19 +620,42 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
516
620
|
system
|
517
621
|
end
|
518
622
|
|
623
|
+
##
|
624
|
+
#Returns an array of all systems, pre-loading relations to reduce the need for extra queries
|
625
|
+
#
|
626
|
+
#Relations are not cached between calls.
|
627
|
+
def self.all_with_relations
|
628
|
+
systems = all
|
629
|
+
system_types = {}
|
630
|
+
systems.each do |system|
|
631
|
+
system_type = system_types[system.system_type_id]
|
632
|
+
if system_type
|
633
|
+
system.system_type = system_type
|
634
|
+
else
|
635
|
+
system_type = system.system_type
|
636
|
+
system_types[system_type.id] = system_type
|
637
|
+
end
|
638
|
+
end
|
639
|
+
systems
|
640
|
+
end
|
641
|
+
|
519
642
|
##
|
520
643
|
#Produces a summary of all the systems for the given time interval
|
521
644
|
#
|
522
645
|
#Returns a hash with the following fields:
|
523
|
-
#- <tt>:
|
524
|
-
#- <tt>:
|
525
|
-
#- <tt>:
|
646
|
+
#- [<tt>:systems</tt>] an array containing all systems that are active in the interval
|
647
|
+
#- [<tt>:avail_cpu_time</tt>] the total CPU time available for the interval
|
648
|
+
#- [<tt>:avail_memory_time</tt>] the total amount of memory-time available (in kilobyte-seconds)
|
649
|
+
#- [<tt>:avail_memory_avg</tt>] the average amount of memory available (in kilobytes)
|
526
650
|
#
|
527
651
|
#To consider: include the start/end times for the summary (especially if they aren't provided as arguments)?
|
528
652
|
#
|
529
|
-
#
|
530
|
-
#
|
653
|
+
#Notes:
|
654
|
+
#
|
655
|
+
#Results may be slightly off when an inclusive range is used.
|
656
|
+
#To consider: is this worth fixing?
|
531
657
|
def self.summary(time_range = nil)
|
658
|
+
#To consider: how to handle time zones with Rails apps?
|
532
659
|
current_time = Time.now
|
533
660
|
#Sums that are actually returned
|
534
661
|
avail_cpu_time = 0
|
@@ -537,14 +664,13 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
537
664
|
systems = System
|
538
665
|
if time_range
|
539
666
|
time_range = time_range.normalized
|
540
|
-
#To
|
541
|
-
systems = systems.
|
542
|
-
'systems.start_time < ? AND (systems.end_time IS NULL OR systems.end_time > ?)',
|
543
|
-
time_range.last,
|
544
|
-
time_range.first)
|
667
|
+
#To do: unit test.
|
668
|
+
systems = systems.by_time_range_inclusive(time_range)
|
545
669
|
end
|
670
|
+
|
671
|
+
all_systems = systems.all_with_relations
|
546
672
|
|
547
|
-
|
673
|
+
all_systems.each do |system|
|
548
674
|
system_start_time = system.start_time
|
549
675
|
system_end_time = system.end_time
|
550
676
|
#Is there a time range constraint?
|
@@ -579,6 +705,7 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
579
705
|
end
|
580
706
|
|
581
707
|
{
|
708
|
+
:systems => all_systems,
|
582
709
|
:avail_cpu_time => avail_cpu_time,
|
583
710
|
:avail_memory_time => avail_memory_time,
|
584
711
|
:avail_memory_avg => if wall_time_range == 0 then 0.0 else Float(avail_memory_time) / wall_time_range end,
|
@@ -721,7 +848,6 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
721
848
|
t.datetime :start_time, :null => false
|
722
849
|
t.datetime :end_time
|
723
850
|
t.integer :cores, :null => false
|
724
|
-
#To consider: replace with a float? (more compact)
|
725
851
|
t.integer :memory, :null => false, :limit => 8
|
726
852
|
end
|
727
853
|
change_table :systems do |t|
|
@@ -766,12 +892,14 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
766
892
|
t.integer :memory, :null => false
|
767
893
|
t.integer :exit_code, :null => false
|
768
894
|
end
|
895
|
+
#To do: more indices?
|
769
896
|
change_table :jobs do |t|
|
770
897
|
t.index :user_id
|
771
898
|
t.index :system_id
|
772
899
|
t.index :command_name
|
773
900
|
t.index :start_time
|
774
901
|
t.index :end_time
|
902
|
+
t.index :exit_code
|
775
903
|
end
|
776
904
|
end
|
777
905
|
|
@@ -787,13 +915,10 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
787
915
|
t.references :system, :null => false
|
788
916
|
t.date :date, :null => false
|
789
917
|
t.string :command_name, :null => false
|
790
|
-
t.integer :num_jobs, :null => false
|
791
918
|
t.integer :cpu_time, :null => false
|
792
919
|
t.integer :memory_time, :null => false
|
793
|
-
t.integer :successful, :null => false
|
794
920
|
end
|
795
921
|
change_table :job_summaries do |t|
|
796
|
-
#To consider: reorder for optimum efficiency?
|
797
922
|
t.index [:date, :user_id, :system_id, :command_name], :unique => true, :name => 'identity'
|
798
923
|
t.index :command_name
|
799
924
|
t.index :date
|
@@ -857,63 +982,4 @@ Please make sure that all previous systems with this hostname have been marked a
|
|
857
982
|
end
|
858
983
|
end
|
859
984
|
|
860
|
-
##
|
861
|
-
#Reopened to add some useful methods
|
862
|
-
class Range
|
863
|
-
##
|
864
|
-
#If end < begin, returns an empty range (begin ... begin)
|
865
|
-
#Otherwise, returns the original range
|
866
|
-
def normalized
|
867
|
-
return self.begin ... self.begin if self.end < self.begin
|
868
|
-
self
|
869
|
-
end
|
870
|
-
|
871
|
-
##
|
872
|
-
#Returns the empty status of the range
|
873
|
-
#
|
874
|
-
#A range is empty if end < begin or if begin == end and exclude_end? is true.
|
875
|
-
def empty?
|
876
|
-
(self.end < self.begin) || (exclude_end? && (self.begin == self.end))
|
877
|
-
end
|
878
985
|
|
879
|
-
#This code probably works, but we're not using it anywhere.
|
880
|
-
# def intersection(other)
|
881
|
-
# self_n = self.normalized
|
882
|
-
# other = other.normalized
|
883
|
-
#
|
884
|
-
# new_begin, new_end, exclude_end = nil
|
885
|
-
#
|
886
|
-
# if self_n.cover?(other.begin)
|
887
|
-
# new_first = other.begin
|
888
|
-
# elsif other.cover?(self_n.begin)
|
889
|
-
# new_first = self_n.begin
|
890
|
-
# end
|
891
|
-
#
|
892
|
-
# return self_n.begin ... self_n.begin unless new_first
|
893
|
-
#
|
894
|
-
# if self_n.cover?(other.end)
|
895
|
-
# unless other.exclude_end? && other.end == self_n.begin
|
896
|
-
# new_end = other.end
|
897
|
-
# exclude_end = other.exclude_end?
|
898
|
-
# end
|
899
|
-
# elsif other.cover?(self_n.end)
|
900
|
-
# unless self_n.exclude_end? && self_n.end == other.begin
|
901
|
-
# new_end = self_n.end
|
902
|
-
# exclude_end = self_n.exclude_end?
|
903
|
-
# end
|
904
|
-
# end
|
905
|
-
#
|
906
|
-
# #If we still haven't found new_end, try one more case:
|
907
|
-
# unless new_end
|
908
|
-
# if self_n.end == other.end
|
909
|
-
# #We'll only get here if both ranges exclude their ends and have the same end.
|
910
|
-
# new_end = self_n.end
|
911
|
-
# exclude_end = true
|
912
|
-
# end
|
913
|
-
# end
|
914
|
-
#
|
915
|
-
# return self_n.begin ... self_n.begin unless new_end
|
916
|
-
#
|
917
|
-
# Range.new(new_begin, new_end, exclude_end)
|
918
|
-
# end
|
919
|
-
end
|