bookie_accounting 1.0.0 → 1.1.0
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.
- 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
|