bookie_accounting 1.2.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -3
  3. data/README.md +4 -24
  4. data/Rakefile +9 -116
  5. data/bin/bookie-data +48 -7
  6. data/bin/bookie-send +6 -14
  7. data/bookie_accounting.gemspec +4 -3
  8. data/lib/bookie/database/group.rb +33 -0
  9. data/lib/bookie/database/job.rb +201 -0
  10. data/lib/bookie/database/job_summary.rb +268 -0
  11. data/lib/bookie/database/lock.rb +36 -0
  12. data/lib/bookie/database/system.rb +166 -0
  13. data/lib/bookie/database/system_type.rb +80 -0
  14. data/lib/bookie/database/user.rb +54 -0
  15. data/lib/bookie/database.rb +7 -805
  16. data/lib/bookie/extensions.rb +23 -44
  17. data/lib/bookie/formatter.rb +8 -4
  18. data/lib/bookie/sender.rb +12 -14
  19. data/lib/bookie/version.rb +1 -1
  20. data/snapshot/test_config.json +2 -2
  21. data/spec/config_spec.rb +2 -2
  22. data/spec/database/group_spec.rb +36 -0
  23. data/spec/database/job_spec.rb +308 -0
  24. data/spec/database/job_summary_spec.rb +302 -0
  25. data/spec/database/lock_spec.rb +41 -0
  26. data/spec/database/migration_spec.rb +44 -0
  27. data/spec/database/system_spec.rb +232 -0
  28. data/spec/database/system_type_spec.rb +68 -0
  29. data/spec/database/user_spec.rb +69 -0
  30. data/spec/formatter_spec.rb +44 -37
  31. data/spec/{comma_dump_formatter_spec.rb → formatters/comma_dump_spec.rb} +16 -30
  32. data/spec/formatters/spreadsheet_spec.rb +98 -0
  33. data/spec/{stdout_formatter_spec.rb → formatters/stdout_spec.rb} +15 -29
  34. data/spec/sender_spec.rb +92 -66
  35. data/spec/{standalone_sender_spec.rb → senders/standalone_spec.rb} +10 -9
  36. data/spec/{torque_cluster_sender_spec.rb → senders/torque_cluster_spec.rb} +9 -13
  37. data/spec/spec_helper.rb +111 -57
  38. data/todo.txt +13 -0
  39. metadata +38 -23
  40. data/rpm/activesupport.erb +0 -151
  41. data/rpm/bundle.erb +0 -71
  42. data/rpm/default.erb +0 -147
  43. data/rpm/mysql2.erb +0 -149
  44. data/rpm/pacct.erb +0 -147
  45. data/rpm/rspec-core.erb +0 -149
  46. data/rpm/sqlite3.erb +0 -147
  47. data/spec/database_spec.rb +0 -1078
  48. data/spec/spreadsheet_formatter_spec.rb +0 -114
@@ -1,1078 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Helpers
4
- def self.create_summaries(obj, base_time)
5
- start_time_1 = base_time
6
- end_time_1 = base_time + 3600 * 40
7
- start_time_2 = base_time + 1800
8
- end_time_2 = base_time + (36000 * 2 + 18000)
9
- summaries = {
10
- :all => obj.summary,
11
- :all_constrained => obj.summary(start_time_1 ... end_time_1),
12
- :clipped => obj.summary(start_time_2 ... end_time_2),
13
- :empty => obj.summary(Time.at(0) ... Time.at(0)),
14
- }
15
- if obj.respond_to?(:by_command_name)
16
- summaries[:all_filtered] = obj.by_command_name('vi').summary(start_time_1 ... end_time_1)
17
- end
18
-
19
- summaries
20
- end
21
-
22
- def test_job_relations(job, relations)
23
- #Make sure all relations with the same value have the same object_id:
24
- rels = [job.user, job.user.group, job.system, job.system.system_type]
25
- unbound_object_id = Object.instance_method(:object_id)
26
- rels.each do |r|
27
- if relations.include?(r)
28
- relations[r].should eql unbound_object_id.bind(r).call
29
- else
30
- relations[r] = unbound_object_id.bind(r).call
31
- end
32
- end
33
- end
34
-
35
- def test_system_relations(system, relations)
36
- t = system.system_type
37
- unbound_object_id = Object.instance_method(:object_id)
38
- if relations.include?(t)
39
- relations[t].should eql unbound_object_id.bind(t).call
40
- else
41
- relations[t] = unbound_object_id.bind(t).call
42
- end
43
- end
44
-
45
-
46
- def check_job_sums(js_sum, j_sum)
47
- [:cpu_time, :memory_time].each do |field|
48
- js_sum[field].should eql j_sum[field]
49
- end
50
- true
51
- end
52
- end
53
-
54
- describe Bookie::Database do
55
- before(:all) do
56
- unless @generated
57
- Bookie::Database::Migration.up
58
- Helpers::generate_database
59
- @generated = true
60
- end
61
- end
62
-
63
- after(:all) do
64
- Bookie::Database::Migration.down
65
- end
66
-
67
- describe Bookie::Database::Lock do
68
- it "finds locks" do
69
- Lock = Bookie::Database::Lock
70
- Lock[:users].should_not eql nil
71
- Lock[:users].name.should eql 'users'
72
- Lock[:groups].should_not eql nil
73
- Lock[:groups].name.should eql 'groups'
74
- expect { Lock[:dummy] }.to raise_error("Unable to find lock 'dummy'")
75
- end
76
-
77
- it "locks records (will probably fail if the testing DB doesn't support row locks)" #do
78
- #lock = Bookie::Database::Lock[:users]
79
- #thread = nil
80
- #lock.synchronize do
81
- # thread = Thread.new {
82
- # t = Time.now
83
- # ActiveRecord::Base.connection_pool.with_connection do
84
- # lock.synchronize do
85
- # Bookie::Database::User.first
86
- # end
87
- # end
88
- # (Time.now - t).should >= 0.5
89
- # }
90
- # sleep(1)
91
- #end
92
- #thread.join
93
- #end
94
-
95
- it "validates fields" do
96
- lock = Bookie::Database::Lock.new
97
- lock.name = nil
98
- lock.valid?.should eql false
99
- lock.name = ''
100
- lock.valid?.should eql false
101
- lock.name = 'test'
102
- lock.valid?.should eql true
103
- end
104
- end
105
-
106
- describe Bookie::Database::Job do
107
- before(:each) do
108
- @jobs = Bookie::Database::Job
109
- end
110
-
111
- it "correctly sets end times" do
112
- @jobs.find_each do |job|
113
- job.end_time.should eql job.start_time + job.wall_time
114
- job.end_time.should eql job.read_attribute(:end_time)
115
- end
116
- #Test the update hook.
117
- job = Bookie::Database::Job.first
118
- job.start_time = job.start_time - 1
119
- job.save!
120
- job.end_time.should eql job.read_attribute(:end_time)
121
- job.start_time = job.start_time + 1
122
- job.save!
123
- end
124
-
125
- it "correctly filters by user" do
126
- user = Bookie::Database::User.by_name('test').order(:id).first
127
- jobs = @jobs.by_user(user).to_a
128
- jobs.each do |job|
129
- job.user.should eql user
130
- end
131
- jobs.length.should eql 10
132
- end
133
-
134
- it "correctly filters by user name" do
135
- jobs = @jobs.by_user_name('root').to_a
136
- jobs.length.should eql 10
137
- jobs[0].user.name.should eql "root"
138
- jobs = @jobs.by_user_name('test').order(:end_time).to_a
139
- jobs.length.should eql 20
140
- jobs.each do |job|
141
- job.user.name.should eql 'test'
142
- end
143
- jobs[0].user_id.should_not eql jobs[-1].user_id
144
- jobs = @jobs.by_user_name('user').to_a
145
- jobs.length.should eql 0
146
- end
147
-
148
- it "correctly filters by group name" do
149
- jobs = @jobs.by_group_name("root").to_a
150
- jobs.length.should eql 10
151
- jobs.each do |job|
152
- job.user.group.name.should eql "root"
153
- end
154
- jobs = @jobs.by_group_name("admin").order(:start_time).to_a
155
- jobs.length.should eql 20
156
- jobs[0].user.name.should_not eql jobs[1].user.name
157
- jobs = @jobs.by_group_name("test").to_a
158
- jobs.length.should eql 0
159
- end
160
-
161
- it "correctly filters by system" do
162
- sys = Bookie::Database::System.first
163
- jobs = @jobs.by_system(sys)
164
- jobs.length.should eql 10
165
- jobs.each do |job|
166
- job.system.should eql sys
167
- end
168
- end
169
-
170
- it "correctly filters by system name" do
171
- jobs = @jobs.by_system_name('test1')
172
- jobs.length.should eql 20
173
- jobs = @jobs.by_system_name('test2')
174
- jobs.length.should eql 10
175
- jobs = @jobs.by_system_name('test3')
176
- jobs.length.should eql 10
177
- jobs = @jobs.by_system_name('test4')
178
- jobs.length.should eql 0
179
- end
180
-
181
- it "correctly filters by system type" do
182
- sys_type = Bookie::Database::SystemType.find_by_name('Standalone')
183
- jobs = @jobs.by_system_type(sys_type)
184
- jobs.length.should eql 20
185
- sys_type = Bookie::Database::SystemType.find_by_name('TORQUE cluster')
186
- jobs = @jobs.by_system_type(sys_type)
187
- jobs.length.should eql 20
188
- end
189
-
190
- it "correctly filters by command name" do
191
- jobs = @jobs.by_command_name('vi')
192
- jobs.length.should eql 20
193
- jobs = @jobs.by_command_name('emacs')
194
- jobs.length.should eql 20
195
- end
196
-
197
- it "correctly filters by start time" do
198
- jobs = @jobs.by_start_time_range(base_time ... base_time + 3600 * 2 + 1)
199
- jobs.length.should eql 3
200
- jobs = @jobs.by_start_time_range(base_time + 1 ... base_time + 3600 * 2)
201
- jobs.length.should eql 1
202
- jobs = @jobs.by_start_time_range(Time.at(0) ... Time.at(3))
203
- jobs.length.should eql 0
204
- end
205
-
206
- it "correctly filters by end time" do
207
- jobs = @jobs.by_end_time_range(base_time ... base_time + 3600 * 2 + 1)
208
- jobs.length.should eql 2
209
- jobs = @jobs.by_end_time_range(base_time + 1 ... base_time + 3600 * 2)
210
- jobs.length.should eql 1
211
- jobs = @jobs.by_end_time_range(Time.at(0) ... Time.at(3))
212
- jobs.length.should eql 0
213
- end
214
-
215
- describe "#by_time_range_inclusive" do
216
- it "correctly filters by inclusive time range" do
217
- jobs = @jobs.by_time_range_inclusive(base_time ... base_time + 3600 * 2 + 1)
218
- jobs.count.should eql 3
219
- jobs = @jobs.by_time_range_inclusive(base_time + 1 ... base_time + 3600 * 2)
220
- jobs.count.should eql 2
221
- jobs = @jobs.by_time_range_inclusive(base_time ... base_time)
222
- jobs.length.should eql 0
223
- jobs = @jobs.by_time_range_inclusive(base_time .. base_time + 3600 * 2)
224
- jobs.count.should eql 3
225
- jobs = @jobs.by_time_range_inclusive(base_time .. base_time)
226
- jobs.count.should eql 1
227
- end
228
-
229
- it "correctly handles empty/inverted ranges" do
230
- t = base_time
231
- (-1 .. 0).each do |offset|
232
- jobs = @jobs.by_time_range_inclusive(t ... t + offset)
233
- jobs.count.should eql 0
234
- end
235
- end
236
- end
237
-
238
- it "correctly chains filters" do
239
- jobs = @jobs.by_user_name("test")
240
- jobs = jobs.by_start_time_range(base_time + 3600 ... base_time + 3601)
241
- jobs.length.should eql 1
242
- jobs[0].user.group.name.should eql "default"
243
- end
244
-
245
- describe "#all_with_relations" do
246
- it "loads all relations" do
247
- jobs = Bookie::Database::Job.limit(5)
248
- relations = {}
249
- jobs = jobs.all_with_relations
250
- Bookie::Database::User.expects(:new).never
251
- Bookie::Database::Group.expects(:new).never
252
- Bookie::Database::System.expects(:new).never
253
- Bookie::Database::SystemType.expects(:new).never
254
- jobs.each do |job|
255
- test_job_relations(job, relations)
256
- end
257
- end
258
- end
259
-
260
- describe "#summary" do
261
- before(:all) do
262
- @jobs = Bookie::Database::Job
263
- @length = @jobs.count
264
- @summary = Helpers::create_summaries(@jobs, base_time)
265
- end
266
-
267
- it "produces correct summary totals" do
268
- @summary[:all][:jobs].length.should eql @length
269
- @summary[:all][:cpu_time].should eql @length * 100
270
- @summary[:all][:memory_time].should eql @length * 200 * 3600
271
- @summary[:all][:successful].should eql 20
272
- @summary[:all_constrained][:jobs].length.should eql @length
273
- @summary[:all_constrained][:cpu_time].should eql @length * 100
274
- @summary[:all_constrained][:successful].should eql 20
275
- @summary[:all_filtered][:jobs].length.should eql @length / 2
276
- @summary[:all_filtered][:cpu_time].should eql @length * 100 / 2
277
- @summary[:all_filtered][:successful].should eql 20
278
- clipped_jobs = @summary[:clipped][:jobs].length
279
- clipped_jobs.should eql 25
280
- @summary[:clipped][:cpu_time].should eql clipped_jobs * 100 - 50
281
- @summary[:clipped][:memory_time].should eql clipped_jobs * 200 * 3600 - 100 * 3600
282
- @summary[:clipped][:successful].should eql clipped_jobs / 2 + 1
283
- end
284
-
285
- it "correctly handles summaries of empty sets" do
286
- @summary[:empty].should eql({
287
- :jobs => [],
288
- :cpu_time => 0,
289
- :memory_time => 0,
290
- :successful => 0,
291
- })
292
- end
293
-
294
- it "correctly handles summaries with zero wall time" do
295
- job = @jobs.order(:start_time).first
296
- wall_time = job.wall_time
297
- begin
298
- job.wall_time = 0
299
- job.save!
300
- @jobs.order(:start_time).limit(1).summary[:cpu_time].should eql 0
301
- ensure
302
- job.wall_time = wall_time
303
- job.save!
304
- end
305
- end
306
-
307
- it "correctly handles inverted ranges" do
308
- @jobs.summary(Time.now() ... Time.now() - 1).should eql @summary[:empty]
309
- @jobs.summary(Time.now() .. Time.now() - 1).should eql @summary[:empty]
310
- end
311
-
312
- it "distinguishes between inclusive and exclusive ranges" do
313
- sum = @jobs.summary(base_time ... base_time + 3600)
314
- sum[:jobs].length.should eql 1
315
- sum = @jobs.summary(base_time .. base_time + 3600)
316
- sum[:jobs].length.should eql 2
317
- end
318
- end
319
-
320
- it "validates fields" do
321
- fields = {
322
- :user => Bookie::Database::User.first,
323
- :system => Bookie::Database::System.first,
324
- :command_name => '',
325
- :cpu_time => 100,
326
- :start_time => base_time,
327
- :wall_time => 1000,
328
- :memory => 10000,
329
- :exit_code => 0
330
- }
331
-
332
- job = Bookie::Database::Job.new(fields)
333
- job.valid?.should eql true
334
-
335
- fields.each_key do |field|
336
- job = Bookie::Database::Job.new(fields)
337
- job.method("#{field}=".intern).call(nil)
338
- job.valid?.should eql false
339
- end
340
-
341
- [:cpu_time, :wall_time, :memory].each do |field|
342
- job = Bookie::Database::Job.new(fields)
343
- m = job.method("#{field}=".intern)
344
- m.call(-1)
345
- job.valid?.should eql false
346
- m.call(0)
347
- job.valid?.should eql true
348
- end
349
- end
350
- end
351
-
352
- describe Bookie::Database::JobSummary do
353
-
354
- describe "" do
355
- before(:all) do
356
- d = Date.new(2012)
357
- Bookie::Database::User.find_each do |user|
358
- Bookie::Database::System.find_each do |system|
359
- ['vi', 'emacs'].each do |command_name|
360
- (d ... d + 2).each do |date|
361
- Bookie::Database::JobSummary.create!(
362
- :user => user,
363
- :system => system,
364
- :command_name => command_name,
365
- :date => date,
366
- :cpu_time => 0,
367
- :memory_time => 0,
368
- :successful => 0
369
- )
370
- end
371
- end
372
- end
373
- end
374
- end
375
-
376
- it "correctly filters by date" do
377
- d = Date.new(2012)
378
- sums = Bookie::Database::JobSummary.by_date(d).to_a
379
- sums.length.should eql 32
380
- sums.each do |sum|
381
- sum.date.should eql d
382
- end
383
- end
384
-
385
- it "correctly filters by date range" do
386
- d = Date.new(2012)
387
- sums = Bookie::Database::JobSummary
388
- sums.by_date_range(d .. d).count.should eql sums.by_date(d).count
389
- sums.by_date_range(d ... d).count.should eql 0
390
- sums.by_date_range(d + 1 .. d).count.should eql 0
391
- sums.by_date_range(d .. d + 1).count.should eql sums.by_date(d).count + sums.by_date(d + 1).count
392
- sums.by_date_range(d ... d + 1).count.should eql sums.by_date(d).count
393
- end
394
-
395
- it "correctly filters by user" do
396
- u = Bookie::Database::User.first
397
- sums = Bookie::Database::JobSummary.by_user(u).to_a
398
- sums.length.should eql 16
399
- sums.each do |sum|
400
- sum.user.should eql u
401
- end
402
- end
403
-
404
- it "correctly filters by user name" do
405
- sums = Bookie::Database::JobSummary.by_user_name('test').to_a
406
- sums.length.should eql 32
407
- sums.each do |sum|
408
- sum.user.name.should eql 'test'
409
- end
410
- end
411
-
412
- it "correctly filters by group" do
413
- g = Bookie::Database::Group.find_by_name('admin')
414
- sums = Bookie::Database::JobSummary.by_group(g).to_a
415
- sums.length.should eql 32
416
- sums.each do |sum|
417
- sum.user.group.should eql g
418
- end
419
- end
420
-
421
- it "correctly filters by group name" do
422
- sums = Bookie::Database::JobSummary.by_group_name('admin').to_a
423
- sums.length.should eql 32
424
- sums.each do |sum|
425
- sum.user.group.name.should eql 'admin'
426
- end
427
- Bookie::Database::JobSummary.by_group_name('fake_group').count.should eql 0
428
- end
429
-
430
- it "correctly filters by system" do
431
- s = Bookie::Database::System.first
432
- sums = Bookie::Database::JobSummary.by_system(s).to_a
433
- sums.length.should eql 16
434
- sums.each do |sum|
435
- sum.system.should eql s
436
- end
437
- end
438
-
439
- it "correctly filters by system name" do
440
- sums = Bookie::Database::JobSummary.by_system_name('test1').to_a
441
- sums.length.should eql 32
442
- sums.each do |sum|
443
- sum.system.name.should eql 'test1'
444
- end
445
- end
446
-
447
- it "correctly filters by system type" do
448
- s = Bookie::Database::SystemType.first
449
- sums = Bookie::Database::JobSummary.by_system_type(s).to_a
450
- sums.length.should eql 32
451
- sums.each do |sum|
452
- sum.system.system_type.should eql s
453
- end
454
- end
455
-
456
- it "correctly filters by command name" do
457
- sums = Bookie::Database::JobSummary.by_command_name('vi').to_a
458
- sums.length.should eql 32
459
- sums.each do |sum|
460
- sum.command_name.should eql 'vi'
461
- end
462
- end
463
- end
464
-
465
- describe "#find_or_new" do
466
- it "creates a summary if needed" do
467
- Bookie::Database::JobSummary.delete_all
468
- s = Bookie::Database::JobSummary.find_or_new(Date.new(2012), 1, 1, 'vi')
469
- s.persisted?.should eql false
470
- s.cpu_time = 0
471
- s.memory_time = 0
472
- s.save!
473
- end
474
-
475
- it "uses the old summary if present" do
476
- #Uses the JobSummary created in the previous test
477
- s = Bookie::Database::JobSummary.find_or_new(Date.new(2012), 1, 1, 'vi')
478
- s.persisted?.should eql true
479
- end
480
- end
481
-
482
- describe "#summarize" do
483
- before(:each) do
484
- Bookie::Database::JobSummary.delete_all
485
- end
486
-
487
- it "produces correct summaries" do
488
- d = Date.new(2012)
489
- range = base_time ... base_time + 1.days
490
- Bookie::Database::JobSummary.summarize(d)
491
- sums = Bookie::Database::JobSummary.all.to_a
492
- found_sums = Set.new
493
- sums.each do |sum|
494
- sum.date.should eql Date.new(2012)
495
- jobs = Bookie::Database::Job.by_user(sum.user).by_system(sum.system).by_command_name(sum.command_name)
496
- sum_2 = jobs.summary(range)
497
- check_job_sums(sum, sum_2)
498
- found_sums.add([sum.user.id, sum.system.id, sum.command_name])
499
- end
500
- #Is it catching all of the combinations of categories?
501
- Bookie::Database::Job.by_time_range_inclusive(range).select('user_id, system_id, command_name').uniq.find_each do |values|
502
- values = [values.user_id, values.system_id, values.command_name]
503
- found_sums.include?(values).should eql true
504
- end
505
- end
506
-
507
- it "creates dummy summaries when there are no jobs" do
508
- d = Date.new(2012) + 5
509
- Bookie::Database::JobSummary.summarize(d)
510
- sums = Bookie::Database::JobSummary.by_date(d).to_a
511
- sums.length.should eql 1
512
- sum = sums[0]
513
- sum.cpu_time.should eql 0
514
- sum.memory_time.should eql 0
515
-
516
- #Check the case where there are no users or no systems:
517
- Bookie::Database::JobSummary.delete_all
518
- [Bookie::Database::User, Bookie::Database::System].each do |klass|
519
- #This will cause nested transactions, but we *should* be OK.
520
- Bookie::Database::JobSummary.transaction do
521
- klass.delete_all
522
- Bookie::Database::JobSummary.summarize(d)
523
- Bookie::Database::JobSummary.by_date(d).count.should eql 0
524
- raise ActiveRecord::Rollback
525
- end
526
- end
527
- end
528
- end
529
-
530
- describe "#summary" do
531
- before(:each) do
532
- Bookie::Database::JobSummary.delete_all
533
- t = base_time + 2.days
534
- Time.expects(:now).at_least(0).returns(t)
535
- end
536
-
537
- it "produces correct summaries" do
538
- #To consider: flesh out some more?
539
- time_start = base_time
540
- time_end = time_start
541
- time_bound = time_start + 3.days
542
- while time_start < time_bound
543
- while time_end < time_bound
544
- [true, false].each do |exclude_end|
545
- time_range = Range.new(time_start, time_end, exclude_end)
546
- sum1 = Bookie::Database::JobSummary.summary(:range => time_range)
547
- sum2 = Bookie::Database::Job.summary(time_range)
548
- check_job_sums(sum1, sum2)
549
- end
550
- time_end += 1.days
551
- end
552
- time_start += 1.days
553
- end
554
- time_start = base_time
555
- time_end = time_start + 1.days
556
- [0, -7200, 7200].each do |offset_begin|
557
- [0, -7200, 7200].each do |offset_end|
558
- [true, false].each do |exclude_end|
559
- range_offset = Range.new(time_start + offset_end, time_end + offset_end, exclude_end)
560
- sum1 = Bookie::Database::JobSummary.summary(:range => range_offset)
561
- sum2 = Bookie::Database::Job.summary(range_offset)
562
- check_job_sums(sum1, sum2)
563
- end
564
- end
565
- end
566
- end
567
-
568
- it "distinguishes between inclusive and exclusive ranges" do
569
- Summary = Bookie::Database::JobSummary
570
- sum = Summary.summary(:range => (base_time ... base_time + 3600 * 2))
571
- sum[:num_jobs].should eql 2
572
- sum = Summary.summary(:range => (base_time .. base_time + 3600 * 2))
573
- sum[:num_jobs].should eql 3
574
- end
575
-
576
- def check_time_bounds(time_max = base_time + 1.days)
577
- time_min = base_time
578
- check_job_sums(Bookie::Database::JobSummary.summary, Bookie::Database::Job.summary)
579
- Bookie::Database::JobSummary.order(:date).first.date.should eql time_min.to_date
580
- Bookie::Database::JobSummary.order('date DESC').first.date.should eql time_max.utc.to_date
581
- end
582
-
583
- it "correctly finds the default time bounds" do
584
- #The last daily summary in this range isn't cached because Time.now could be partway through a day.
585
- check_time_bounds
586
- systems = Bookie::Database::System.active_systems
587
- Bookie::Database::JobSummary.delete_all
588
- #Check the case where all systems are decommissioned.
589
- end_times = {}
590
- begin
591
- systems.each do |sys|
592
- end_times[sys.id] = sys.end_time
593
- sys.end_time = base_time + 2.days
594
- sys.save!
595
- end
596
- check_time_bounds
597
- ensure
598
- systems.each do |sys|
599
- sys.end_time = end_times[sys.id]
600
- sys.save!
601
- end
602
- end
603
- Bookie::Database::JobSummary.delete_all
604
- empty = Bookie::Database::System.limit(0)
605
- #Check the case where there are no systems.
606
- #Stub out methods of System's "Relation" class:
607
- Bookie::Database::System.where('1=1').class.any_instance.expects(:'any?').at_least_once.returns(false)
608
- Bookie::Database::System.where('1=1').class.any_instance.expects(:first).at_least_once.returns(nil)
609
- sum = Bookie::Database::JobSummary.summary
610
- sum.should eql({
611
- :num_jobs => 0,
612
- :cpu_time => 0,
613
- :memory_time => 0,
614
- :successful => 0,
615
- })
616
- Bookie::Database::JobSummary.any?.should eql false
617
- end
618
-
619
- it "correctly handles filtered summaries" do
620
- filters = {
621
- :user_name => 'test',
622
- :group_name => 'admin',
623
- :command_name => 'vi',
624
- }
625
- filters.each do |filter, value|
626
- filter_sym = "by_#{filter}".intern
627
- jobs = Bookie::Database::Job.send(filter_sym, value)
628
- sum1 = Bookie::Database::JobSummary.send(filter_sym, value).summary(:jobs => jobs)
629
- sum2 = jobs.summary
630
- check_job_sums(sum1, sum2)
631
- end
632
- end
633
-
634
- it "correctly handles inverted ranges" do
635
- t = base_time
636
- Bookie::Database::JobSummary.summary(:range => t .. t - 1).should eql({
637
- :num_jobs => 0,
638
- :cpu_time => 0,
639
- :memory_time => 0,
640
- :successful => 0,
641
- })
642
- end
643
-
644
- it "caches summaries" do
645
- Bookie::Database::JobSummary.summary
646
- Bookie::Database::JobSummary.expects(:summarize).never
647
- range = base_time ... base_time + 1.days
648
- Bookie::Database::JobSummary.summary(:range => range)
649
- end
650
-
651
- it "uses the cached summaries" do
652
- Bookie::Database::JobSummary.summary
653
- Bookie::Database::Job.expects(:summary).never
654
- range = base_time ... base_time + 1.days
655
- Bookie::Database::JobSummary.summary(:range => range)
656
- end
657
- end
658
-
659
- it "validates fields" do
660
- fields = {
661
- :user => Bookie::Database::User.first,
662
- :system => Bookie::Database::System.first,
663
- :command_name => '',
664
- :date => Date.new(2012),
665
- :cpu_time => 100,
666
- :memory_time => 1000000,
667
- }
668
-
669
- sum = Bookie::Database::JobSummary.new(fields)
670
- sum.valid?.should eql true
671
-
672
- fields.each_key do |field|
673
- job = Bookie::Database::JobSummary.new(fields)
674
- job.method("#{field}=".intern).call(nil)
675
- job.valid?.should eql false
676
- end
677
-
678
- [:cpu_time, :memory_time].each do |field|
679
- job = Bookie::Database::JobSummary.new(fields)
680
- m = job.method("#{field}=".intern)
681
- m.call(-1)
682
- job.valid?.should eql false
683
- m.call(0)
684
- job.valid?.should eql true
685
- end
686
- end
687
- end
688
-
689
- describe Bookie::Database::User do
690
- it "correctly filters by name" do
691
- users = Bookie::Database::User.by_name('test').to_a
692
- users.length.should eql 2
693
- users.each do |user|
694
- user.name.should eql 'test'
695
- end
696
- end
697
-
698
- it "correctly filters by group" do
699
- Bookie::Database::User.by_group(Bookie::Database::Group.find_by_name('admin')).count.should eql 2
700
- Bookie::Database::User.by_group(Bookie::Database::Group.find_by_name('root')).count.should eql 1
701
- end
702
-
703
- it "correctly filters by group name" do
704
- Bookie::Database::User.by_group_name('admin').count.should eql 2
705
- Bookie::Database::User.by_group_name('fake_group').count.should eql 0
706
- end
707
-
708
- describe "#find_or_create" do
709
- before(:each) do
710
- @group = Bookie::Database::Group.find_by_name('admin')
711
- end
712
-
713
- it "creates the user if needed" do
714
- Bookie::Database::User.expects(:"create!").twice
715
- user = Bookie::Database::User.find_or_create!('me', @group)
716
- user = Bookie::Database::User.find_or_create!('me', @group, {})
717
- end
718
-
719
- it "returns the cached user if one exists" do
720
- user = Bookie::Database::User.find_by_name('root')
721
- known_users = {['root', user.group] => user}
722
- Bookie::Database::User.find_or_create!('root', user.group, known_users).should equal user
723
- end
724
-
725
- it "queries the database when this user is not cached" do
726
- user = Bookie::Database::User.find_by_name_and_group_id('root', 1)
727
- known_users = {}
728
- Bookie::Database::User.expects(:find_by_name_and_group_id).returns(user).twice
729
- Bookie::Database::User.expects(:"create!").never
730
- Bookie::Database::User.find_or_create!('root', user.group, known_users).should eql user
731
- Bookie::Database::User.find_or_create!('root', user.group, nil).should eql user
732
- known_users.should include ['root', user.group]
733
- end
734
- end
735
-
736
- it "validates fields" do
737
- fields = {
738
- :group => Bookie::Database::Group.first,
739
- :name => 'test',
740
- }
741
-
742
- Bookie::Database::User.new(fields).valid?.should eql true
743
-
744
- fields.each_key do |field|
745
- user = Bookie::Database::User.new(fields)
746
- user.method("#{field}=".intern).call(nil)
747
- user.valid?.should eql false
748
- end
749
-
750
- user = Bookie::Database::User.new(fields)
751
- user.name = ''
752
- user.valid?.should eql false
753
- end
754
- end
755
-
756
- describe Bookie::Database::Group do
757
- describe "#find_or_create" do
758
- it "creates the group if needed" do
759
- Bookie::Database::Group.expects(:"create!")
760
- Bookie::Database::Group.find_or_create!('non_root')
761
- end
762
-
763
- it "returns the cached group if one exists" do
764
- group = Bookie::Database::Group.find_by_name('root')
765
- known_groups = {'root' => group}
766
- Bookie::Database::Group.find_or_create!('root', known_groups).should equal group
767
- end
768
-
769
- it "queries the database when this group is not cached" do
770
- group = Bookie::Database::Group.find_by_name('root')
771
- known_groups = {}
772
- Bookie::Database::Group.expects(:find_by_name).returns(group).twice
773
- Bookie::Database::Group.expects(:"create!").never
774
- Bookie::Database::Group.find_or_create!('root', known_groups).should eql group
775
- Bookie::Database::Group.find_or_create!('root', nil).should eql group
776
- known_groups.should include 'root'
777
- end
778
- end
779
-
780
- it "validates the name field" do
781
- group = Bookie::Database::Group.new(:name => nil)
782
- group.valid?.should eql false
783
- group.name = ''
784
- group.valid?.should eql false
785
- group.name = 'test'
786
- group.valid?.should eql true
787
- end
788
- end
789
-
790
- describe Bookie::Database::System do
791
- before(:each) do
792
- @systems = Bookie::Database::System
793
- end
794
-
795
- it "correctly finds active systems" do
796
- Bookie::Database::System.active_systems.length.should eql 3
797
- end
798
-
799
- it "correctly filters by name" do
800
- Bookie::Database::System.by_name('test1').length.should eql 2
801
- Bookie::Database::System.by_name('test2').length.should eql 1
802
- Bookie::Database::System.by_name('test3').length.should eql 1
803
- end
804
-
805
- it "correctly filters by system type" do
806
- ['Standalone', 'TORQUE cluster'].each do |type|
807
- t = Bookie::Database::SystemType.find_by_name(type)
808
- Bookie::Database::System.by_system_type(t).length.should eql 2
809
- end
810
- end
811
-
812
- describe "#all_with_relations" do
813
- it "loads all relations" do
814
- systems = Bookie::Database::System.limit(5)
815
- relations = {}
816
- systems = systems.all_with_relations
817
- Bookie::Database::SystemType.expects(:new).never
818
- systems.each do |system|
819
- test_system_relations(system, relations)
820
- end
821
- end
822
- end
823
-
824
- describe "#by_time_range_inclusive" do
825
- it "correctly filters by inclusive time range" do
826
- systems = @systems.by_time_range_inclusive(base_time ... base_time + 36000 * 2 + 1)
827
- systems.count.should eql 3
828
- systems = @systems.by_time_range_inclusive(base_time + 1 ... base_time + 36000 * 2)
829
- systems.count.should eql 2
830
- systems = @systems.by_time_range_inclusive(base_time ... base_time)
831
- systems.length.should eql 0
832
- systems = @systems.by_time_range_inclusive(base_time .. base_time + 36000 * 2)
833
- systems.count.should eql 3
834
- systems = @systems.by_time_range_inclusive(base_time .. base_time)
835
- systems.count.should eql 1
836
- end
837
-
838
- it "correctly handles empty/inverted ranges" do
839
- (-1 .. 0).each do |offset|
840
- systems = @systems.by_time_range_inclusive(base_time ... base_time + offset)
841
- systems.count.should eql 0
842
- end
843
- end
844
- end
845
-
846
- describe "#summary" do
847
- before(:all) do
848
- Time.expects(:now).returns(base_time + 3600 * 40).at_least_once
849
- @systems = Bookie::Database::System
850
- @summary = Helpers::create_summaries(@systems, base_time)
851
- @summary_wide = @systems.summary(base_time - 3600 ... base_time + 3600 * 40 + 3600)
852
- end
853
-
854
- it "produces correct summaries" do
855
- system_total_wall_time = 3600 * (10 + 30 + 20 + 10)
856
- system_clipped_wall_time = 3600 * (10 + 15 + 5) - 1800
857
- system_wide_wall_time = system_total_wall_time + 3600 * 3
858
- system_total_cpu_time = system_total_wall_time * 2
859
- clipped_cpu_time = system_clipped_wall_time * 2
860
- system_wide_cpu_time = system_wide_wall_time * 2
861
- avg_mem = Float(1000000 * system_total_wall_time / (3600 * 40))
862
- clipped_avg_mem = Float(1000000 * system_clipped_wall_time) / (3600 * 25 - 1800)
863
- wide_avg_mem = Float(1000000 * system_wide_wall_time) / (3600 * 42)
864
- @summary[:all][:systems].length.should eql 4
865
- @summary[:all][:avail_cpu_time].should eql system_total_cpu_time
866
- @summary[:all][:avail_memory_time].should eql 1000000 * system_total_wall_time
867
- @summary[:all][:avail_memory_avg].should eql avg_mem
868
- @summary[:all_constrained][:systems].length.should eql 4
869
- @summary[:all_constrained][:avail_cpu_time].should eql system_total_cpu_time
870
- @summary[:all_constrained][:avail_memory_time].should eql 1000000 * system_total_wall_time
871
- @summary[:all_constrained][:avail_memory_avg].should eql avg_mem
872
- @summary[:clipped][:systems].length.should eql 3
873
- @summary[:clipped][:avail_cpu_time].should eql clipped_cpu_time
874
- @summary[:clipped][:avail_memory_time].should eql system_clipped_wall_time * 1000000
875
- @summary[:clipped][:avail_memory_avg].should eql clipped_avg_mem
876
- @summary_wide[:systems].length.should eql 4
877
- @summary_wide[:avail_cpu_time].should eql system_wide_cpu_time
878
- @summary_wide[:avail_memory_time].should eql 1000000 * system_wide_wall_time
879
- @summary_wide[:avail_memory_avg].should eql wide_avg_mem
880
- @summary[:empty][:systems].length.should eql 0
881
- @summary[:empty][:avail_cpu_time].should eql 0
882
- @summary[:empty][:avail_memory_time].should eql 0
883
- @summary[:empty][:avail_memory_avg].should eql 0.0
884
- begin
885
- @systems.find_each do |system|
886
- unless system.id == 1
887
- system.end_time = Time.now
888
- system.save!
889
- end
890
- end
891
- summary_all_systems_ended = @systems.summary()
892
- summary_all_systems_ended.should eql @summary[:all]
893
- summary_all_systems_ended = @systems.summary(base_time ... Time.now + 3600)
894
- s2 = @summary[:all].dup
895
- s2[:avail_memory_avg] = Float(1000000 * system_total_wall_time) / (3600 * 41)
896
- summary_all_systems_ended.should eql s2
897
- ensure
898
- @systems.find_each do |system|
899
- unless system.id == 1
900
- system.end_time = nil
901
- system.save!
902
- end
903
- end
904
- end
905
- end
906
-
907
- it "correctly handles inverted ranges" do
908
- t = base_time
909
- @systems.summary(t ... t - 1).should eql @summary[:empty]
910
- @systems.summary(t .. t - 1).should eql @summary[:empty]
911
- end
912
- end
913
-
914
- describe "#find_current" do
915
- before(:all) do
916
- @config_t1 = @config.clone
917
-
918
- @config_t1.hostname = 'test1'
919
- @config_t1.system_type = 'standalone'
920
- @config_t1.cores = 2
921
- @config_t1.memory = 1000000
922
-
923
- @config_t2 = @config_t1.clone
924
- @config_t2.system_type = 'torque_cluster'
925
-
926
- @sender_1 = Bookie::Sender.new(@config_t1)
927
- @sender_2 = Bookie::Sender.new(@config_t2)
928
- end
929
-
930
- it "finds the correct system" do
931
- Bookie::Database::System.find_current(@sender_2).id.should eql 2
932
- Bookie::Database::System.find_current(@sender_2, Time.now).id.should eql 2
933
- Bookie::Database::System.find_current(@sender_1, base_time).id.should eql 1
934
- end
935
-
936
- it "correctly detects the lack of a matching system" do
937
- expect {
938
- Bookie::Database::System.find_current(@sender_1, base_time - 1.years)
939
- }.to raise_error(/^There is no system with hostname 'test1' in the database at /)
940
- @config_t1.expects(:hostname).at_least_once.returns('test1000')
941
- expect {
942
- Bookie::Database::System.find_current(@sender_1, base_time)
943
- }.to raise_error(/^There is no system with hostname 'test1000' in the database at /)
944
- end
945
-
946
- it "correctly detects conflicts" do
947
- config = @config.clone
948
- config.hostname = 'test1'
949
- config.cores = 2
950
- config.memory = 1000000
951
-
952
- sender = Bookie::Sender.new(config)
953
- [:cores, :memory].each do |field|
954
- config.expects(field).at_least_once.returns("value")
955
- expect {
956
- Bookie::Database::System.find_current(sender)
957
- }.to raise_error(Bookie::Database::System::SystemConflictError)
958
- config.unstub(field)
959
- end
960
- sender.expects(:system_type).returns(Bookie::Database::SystemType.find_by_name("Standalone"))
961
- expect {
962
- Bookie::Database::System.find_current(sender)
963
- }.to raise_error(Bookie::Database::System::SystemConflictError)
964
- end
965
- end
966
-
967
- it "correctly decommissions" do
968
- sys = Bookie::Database::System.active_systems.find_by_name('test1')
969
- begin
970
- sys.decommission(sys.start_time + 3)
971
- sys.end_time.should eql sys.start_time + 3
972
- ensure
973
- sys.end_time = nil
974
- sys.save!
975
- end
976
- end
977
-
978
- it "validates fields" do
979
- fields = {
980
- :name => 'test',
981
- :cores => 2,
982
- :memory => 1000000,
983
- :system_type => Bookie::Database::SystemType.first,
984
- :start_time => base_time
985
- }
986
-
987
- Bookie::Database::System.new(fields).valid?.should eql true
988
-
989
- fields.each_key do |field|
990
- system = Bookie::Database::System.new(fields)
991
- system.method("#{field}=".intern).call(nil)
992
- system.valid?.should eql false
993
- end
994
-
995
- system = Bookie::Database::System.new(fields)
996
- system.name = ''
997
- system.valid?.should eql false
998
-
999
- [:cores, :memory].each do |field|
1000
- system = Bookie::Database::System.new(fields)
1001
- m = system.method("#{field}=".intern)
1002
- m.call(-1)
1003
- system.valid?.should eql false
1004
- m.call(0)
1005
- system.valid?.should eql true
1006
- end
1007
-
1008
- system = Bookie::Database::System.new(fields)
1009
- system.end_time = base_time
1010
- system.valid?.should eql true
1011
- system.end_time += 5
1012
- system.valid?.should eql true
1013
- system.end_time -= 10
1014
- system.valid?.should eql false
1015
- end
1016
- end
1017
-
1018
- describe Bookie::Database::SystemType do
1019
- it "correctly maps memory stat type codes to/from symbols" do
1020
- systype = Bookie::Database::SystemType.new
1021
- systype.memory_stat_type = :avg
1022
- systype.memory_stat_type.should eql :avg
1023
- systype.read_attribute(:memory_stat_type).should eql Bookie::Database::MEMORY_STAT_TYPE[:avg]
1024
- systype.memory_stat_type = :max
1025
- systype.memory_stat_type.should eql :max
1026
- systype.read_attribute(:memory_stat_type).should eql Bookie::Database::MEMORY_STAT_TYPE[:max]
1027
- end
1028
-
1029
- it "rejects unrecognized memory stat type codes" do
1030
- systype = Bookie::Database::SystemType.new
1031
- expect { systype.memory_stat_type = :invalid_type }.to raise_error("Unrecognized memory stat type 'invalid_type'")
1032
- expect { systype.memory_stat_type = nil }.to raise_error 'Memory stat type must not be nil'
1033
- systype.send(:write_attribute, :memory_stat_type, 10000)
1034
- expect { systype.memory_stat_type }.to raise_error("Unrecognized memory stat type code 10000")
1035
- end
1036
-
1037
- it "creates the system type when needed" do
1038
- Bookie::Database::SystemType.expects(:'create!')
1039
- Bookie::Database::SystemType.find_or_create!('test', :avg)
1040
- end
1041
-
1042
- it "raises an error if the existing type has the wrong memory stat type" do
1043
- systype = Bookie::Database::SystemType.create!(:name => 'test', :memory_stat_type => :max)
1044
- begin
1045
- expect {
1046
- Bookie::Database::SystemType.find_or_create!('test', :avg)
1047
- }.to raise_error("The recorded memory stat type for system type 'test' does not match the required type of 1")
1048
- expect {
1049
- Bookie::Database::SystemType.find_or_create!('test', :unrecognized)
1050
- }.to raise_error("Unrecognized memory stat type 'unrecognized'")
1051
- ensure
1052
- systype.delete
1053
- end
1054
- end
1055
-
1056
- it "uses the existing type" do
1057
- systype = Bookie::Database::SystemType.create!(:name => 'test', :memory_stat_type => :avg)
1058
- begin
1059
- Bookie::Database::SystemType.expects(:'create!').never
1060
- Bookie::Database::SystemType.find_or_create!('test', :avg)
1061
- ensure
1062
- systype.delete
1063
- end
1064
- end
1065
-
1066
- it "validates fields" do
1067
- systype = Bookie::Database::SystemType.new(:name => 'test')
1068
- expect { systype.valid? }.to raise_error('Memory stat type must not be nil')
1069
- systype.memory_stat_type = :unknown
1070
- systype.valid?.should eql true
1071
- systype.name = nil
1072
- systype.valid?.should eql false
1073
- systype.name = ''
1074
- systype.valid?.should eql false
1075
- end
1076
- end
1077
- end
1078
-