bookie_accounting 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,7 +28,6 @@ module Bookie
28
28
  #An array containing the labels for each field in a summary
29
29
  SUMMARY_FIELD_LABELS = [
30
30
  "Number of jobs",
31
- "Total wall time",
32
31
  "Total CPU time",
33
32
  "Successful",
34
33
  "Available CPU time",
@@ -45,27 +44,28 @@ module Bookie
45
44
  ]
46
45
 
47
46
  ##
48
- #Prints a summary of <tt>jobs</tt> and <tt>systems</tt> to <tt>io</tt>
47
+ #Prints a summary of <tt>jobs</tt> and <tt>systems</tt> to <tt>io</tt>, using cached data from <tt>summaries</tt>
49
48
  #
50
49
  #Use start_time and end_time to filter the jobs by a time range.
51
50
  #
52
- #It is probably not a good idea to apply any time-based filters to <tt>jobs</tt> or <tt>systems</tt> beforehand.
51
+ #It is probably not a good idea to apply any time-based filters to the arguments beforehand.
53
52
  #
54
- #Both <tt>jobs</tt> and <tt>systems</tt> should be either models or ActiveRecord::Relation objects.
53
+ #Both <tt>jobs</tt>, <tt>summaries</tt>, and <tt>systems</tt> should be either models or ActiveRecord::Relation objects.
55
54
  #
56
55
  #Returns the summaries for <tt>jobs</tt> and <tt>systems</tt>
57
- def print_summary(jobs, systems, start_time = nil, end_time = nil)
58
- jobs_summary = jobs.summary(start_time, end_time)
59
- systems_summary = systems.summary(start_time, end_time)
56
+ def print_summary(jobs, summaries, systems, time_range = nil)
57
+ jobs_summary = summaries.summary(:jobs => jobs, :range => time_range)
58
+ num_jobs = jobs_summary[:num_jobs]
59
+ systems_summary = systems.summary(time_range)
60
60
  cpu_time = jobs_summary[:cpu_time]
61
61
  avail_cpu_time = systems_summary[:avail_cpu_time]
62
62
  memory_time = jobs_summary[:memory_time]
63
63
  avail_memory_time = systems_summary[:avail_memory_time]
64
+ successful = (num_jobs == 0) ? 0.0 : Float(jobs_summary[:successful]) / num_jobs
64
65
  field_values = [
65
- jobs_summary[:jobs].length,
66
- Formatter.format_duration(jobs_summary[:wall_time]),
66
+ num_jobs,
67
67
  Formatter.format_duration(cpu_time),
68
- '%.4f%%' % (jobs_summary[:successful] * 100),
68
+ '%.4f%%' % (successful * 100),
69
69
  Formatter.format_duration(systems_summary[:avail_cpu_time]),
70
70
  if avail_cpu_time == 0 then '0.0000%' else '%.4f%%' % (Float(cpu_time) / avail_cpu_time * 100) end,
71
71
  "#{Integer(systems_summary[:avail_memory_avg])} kb",
data/lib/bookie/sender.rb CHANGED
@@ -6,6 +6,8 @@ module Bookie
6
6
  ##
7
7
  #An object that sends data to the database
8
8
  class Sender
9
+ attr_reader :config
10
+
9
11
  ##
10
12
  #Creates a new Sender
11
13
  #
@@ -17,61 +19,66 @@ module Bookie
17
19
  extend Bookie::Senders.const_get(t.camelize)
18
20
  end
19
21
 
20
- ##
21
- #Retrieves the System object with which the jobs will be associated
22
- #--
23
- #To consider: caching?
24
- #++
25
- def system
26
- hostname = @config.hostname
27
- system_type = self.system_type
28
- Bookie::Database::System.find_active(
29
- :name => hostname,
30
- :system_type => system_type,
31
- :start_time => Time.now,
32
- :cores => @config.cores,
33
- :memory => @config.memory
34
- )
35
- end
36
-
37
22
  ##
38
23
  #Sends job data from the given file to the database server
39
24
  def send_data(filename)
40
25
  raise IOError.new("File '#{filename}' does not exist.") unless File.exists?(filename)
41
26
 
42
- system = self.system
27
+ system = nil
43
28
 
44
29
  known_users = {}
45
30
  known_groups = {}
46
31
 
47
- #Check the first job to see if there are entries in the database for its date from this system.
32
+ time_min, time_max = nil
33
+
34
+ #Grab data from the first job:
48
35
  each_job(filename) do |job|
49
36
  next if filtered?(job)
50
37
  end_time = job.start_time + job.wall_time
38
+ system = Bookie::Database::System.find_current(self, end_time)
51
39
  duplicate = system.jobs.find_by_end_time(end_time)
52
- if duplicate
53
- raise "Jobs already exist in the database for the date #{end_time.strftime('%Y-%m-%d')}."
54
- end
40
+ raise "Jobs already exist in the database for '#{filename}'." if duplicate
41
+ time_min = job.start_time
42
+ time_max = end_time
55
43
  break
56
44
  end
57
45
 
46
+ #If there are no jobs, return.
47
+ return unless time_min
48
+
49
+ #To do: add an option to resume an interrupted send.
50
+
51
+ #Send the job data:
58
52
  each_job(filename) do |job|
59
53
  next if filtered?(job)
60
- db_job = job.to_model
61
- #Determine if the user/group pair must be added to/retrieved from the database.
54
+ model = job.to_model
55
+ time_min = (model.start_time < time_min) ? model.start_time : time_min
56
+ time_max = (model.end_time > time_max) ? model.end_time : time_max
57
+ #To consider: handle files that don't have jobs sorted by end time?
58
+ if system.end_time && model.end_time > system.end_time
59
+ system = Database::System.find_current(self, model.end_time)
60
+ end
62
61
  user = Bookie::Database::User.find_or_create!(
63
62
  job.user_name,
64
63
  Bookie::Database::Group.find_or_create!(job.group_name, known_groups),
65
- known_users)
66
- db_job.system = system
67
- db_job.user = user
68
- db_job.save!
64
+ known_users
65
+ )
66
+ model.system = system
67
+ model.user = user
68
+ model.save!
69
69
  end
70
+
71
+ #Clear out the summaries that would have been affected by the new data:
72
+ date_min = time_min.to_date
73
+ date_max = time_max.to_date
74
+
75
+ Database::JobSummary.by_system(system).where('date >= ? AND date <= ?', date_min, date_max).delete_all
70
76
  end
71
77
 
72
78
  ##
73
79
  #The name of the Bookie::Database::SystemType that systems using this sender will have
74
80
  def system_type
81
+ #To consider: cache?
75
82
  Bookie::Database::SystemType.find_or_create!(system_type_name, memory_stat_type)
76
83
  end
77
84
 
@@ -1,4 +1,4 @@
1
1
  module Bookie
2
2
  #The library version
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
@@ -0,0 +1,81 @@
1
+ %define gem_name <%= gem_name %>
2
+
3
+ <% if is_app %>
4
+ Name: %{gem_name}
5
+ <% else %>
6
+ Name: rubygem-%{gem_name}
7
+ <% end %>
8
+ Version: <%= version %>
9
+ Release: 1%{?dist}
10
+ Summary: <%= summary %>
11
+
12
+ %define gem <%= gem %>
13
+
14
+ Group: Development/Libraries
15
+ License: <%= license %>
16
+ URL: <%= url %>
17
+ Source0: <%= gem_file %>
18
+ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
19
+
20
+ BuildRequires: ruby-devel
21
+ Requires: rubygems
22
+ <% if requires.length > 0 %>
23
+ Requires: <%= requires.join(", ") %>
24
+ <% end %>
25
+
26
+ %description
27
+ <%= description %>
28
+
29
+ %prep
30
+ rm -rf %{name}
31
+ mkdir %{name}
32
+ %setup -q -D -T -n %{name}
33
+ cp %{SOURCE0} .
34
+
35
+ %define gem_dir /usr/lib/ruby/gems/<%= ruby_version %>
36
+ %define gem_instdir %{gem_dir}/gems/%{gem}
37
+
38
+ %build
39
+ mkdir -p ./%{gem_dir}
40
+
41
+ export CONFIGURE_ARGS="--with-cflags='%{optflags}'"
42
+ gem install -V \
43
+ --local \
44
+ --install-dir ./%{gem_dir} \
45
+ --bindir ./%{_bindir} \
46
+ --force \
47
+ --rdoc \
48
+ <%= gem_file %>
49
+
50
+ %install
51
+ rm -rf %{buildroot}
52
+ mkdir -p %{buildroot}%{gem_instdir}
53
+
54
+ <% if has_binaries %>
55
+ cp -a ./%{_bindir} %{buildroot}%{_bindir}/
56
+ <% end %>
57
+
58
+ cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/
59
+
60
+ mkdir -p %{buildroot}/etc/bookie/
61
+ echo "{}" > %{buildroot}/etc/bookie/config.json
62
+
63
+ %clean
64
+ rm -rf %{buildroot}
65
+
66
+ %files
67
+ %defattr(-,root,root,-)
68
+ %dir %{gem_dir}/doc/%{gem}
69
+ %doc %{gem_dir}/doc/%{gem}/*
70
+ %dir %{gem_instdir}
71
+ <% if has_binaries %>
72
+ %{_bindir}/*
73
+ <% end %>
74
+ /etc/bookie/config.json
75
+ %{gem_instdir}/*
76
+ %{gem_instdir}/.*
77
+ %{gem_dir}/specifications/%{gem}.gemspec
78
+ %{gem_dir}/cache/%{gem}.gem
79
+
80
+ %changelog
81
+
@@ -0,0 +1,77 @@
1
+ %define gem_name <%= gem_name %>
2
+
3
+ <% if is_app %>
4
+ Name: %{gem_name}
5
+ <% else %>
6
+ Name: rubygem-%{gem_name}
7
+ <% end %>
8
+ Version: <%= version %>
9
+ Release: 1%{?dist}
10
+ Summary: <%= summary %>
11
+
12
+ %define gem <%= gem %>
13
+
14
+ Group: Development/Libraries
15
+ License: <%= license %>
16
+ URL: <%= url %>
17
+ Source0: <%= gem_file %>
18
+ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
19
+
20
+ BuildRequires: ruby-devel
21
+ Requires: rubygems
22
+ <% if requires.length > 0 %>
23
+ Requires: <%= requires.join(", ") %>
24
+ <% end %>
25
+
26
+ %description
27
+ <%= description %>
28
+
29
+ %prep
30
+ rm -rf %{name}
31
+ mkdir %{name}
32
+ %setup -q -D -T -n %{name}
33
+ cp %{SOURCE0} .
34
+
35
+ %define gem_dir /usr/lib/ruby/gems/<%= ruby_version %>
36
+ %define gem_instdir %{gem_dir}/gems/%{gem}
37
+
38
+ %build
39
+ mkdir -p ./%{gem_dir}
40
+
41
+ export CONFIGURE_ARGS="--with-cflags='%{optflags}'"
42
+ gem install -V \
43
+ --local \
44
+ --install-dir ./%{gem_dir} \
45
+ --bindir ./%{_bindir} \
46
+ --force \
47
+ --rdoc \
48
+ <%= gem_file %>
49
+
50
+ %install
51
+ rm -rf %{buildroot}
52
+ mkdir -p %{buildroot}%{gem_instdir}
53
+
54
+ <% if has_binaries %>
55
+ cp -a ./%{_bindir} %{buildroot}%{_bindir}/
56
+ <% end %>
57
+
58
+ cp -a ./%{gem_dir}/* %{buildroot}%{gem_dir}/
59
+
60
+ %clean
61
+ rm -rf %{buildroot}
62
+
63
+ %files
64
+ %defattr(-,root,root,-)
65
+ %dir %{gem_dir}/doc/%{gem}
66
+ %doc %{gem_dir}/doc/%{gem}/*
67
+ %dir %{gem_instdir}
68
+ <% if has_binaries %>
69
+ %{_bindir}/*
70
+ <% end %>
71
+ %{gem_instdir}/*
72
+ %{gem_instdir}/.*
73
+ %{gem_dir}/specifications/%{gem}.gemspec
74
+ %{gem_dir}/cache/%{gem}.gem
75
+
76
+ %changelog
77
+
@@ -13,6 +13,7 @@ describe Bookie::Formatters::CommaDump do
13
13
  Bookie::Database::Migration.up
14
14
  Helpers::generate_database
15
15
  @jobs = Bookie::Database::Job
16
+ @summaries = Bookie::Database::JobSummary
16
17
  end
17
18
 
18
19
  before(:each) do
@@ -31,7 +32,7 @@ describe Bookie::Formatters::CommaDump do
31
32
  end
32
33
 
33
34
  it "correctly formats jobs" do
34
- @formatter.print_jobs(@jobs.order(:start_time).limit(2))
35
+ @formatter.print_jobs(@jobs.order(:start_time).limit(2).all)
35
36
  @m.buf.should eql <<-eos
36
37
  User, Group, System, System type, Start time, End time, Wall time, CPU time, Memory usage, Exit code
37
38
  root, root, test1, Standalone, 2012-01-01 00:00:00, 2012-01-01 01:00:00, 01:00:00, 00:01:40, 200kb (avg), 0
@@ -41,16 +42,15 @@ eos
41
42
 
42
43
  it "correctly formats summaries" do
43
44
  Time.expects(:now).returns(Time.local(2012) + 36000 * 4).at_least_once
44
- @formatter.print_summary(@jobs.order(:start_time).limit(5), Bookie::Database::System)
45
+ @formatter.print_summary(@jobs, @summaries, Bookie::Database::System)
45
46
  @m.buf.should eql <<-eos
46
- Number of jobs, 5
47
- Total wall time, 05:00:00
48
- Total CPU time, 00:08:20
49
- Successful, 60.0000%
47
+ Number of jobs, 40
48
+ Total CPU time, 01:06:40
49
+ Successful, 50.0000%
50
50
  Available CPU time, 140:00:00
51
- CPU time used, 0.0992%
51
+ CPU time used, 0.7937%
52
52
  Available memory (average), 1750000 kb
53
- Memory used (average), 0.0014%
53
+ Memory used (average), 0.0114%
54
54
  eos
55
55
  end
56
56
  end
@@ -6,12 +6,17 @@ module Helpers
6
6
  end_time_1 = base_time + 3600 * 40
7
7
  start_time_2 = base_time + 1800
8
8
  end_time_2 = base_time + (36000 * 2 + 18000)
9
- {
9
+ summaries = {
10
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)),
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
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
15
20
  end
16
21
 
17
22
  def test_relations(job, relations)
@@ -24,6 +29,14 @@ module Helpers
24
29
  relations[r] = r
25
30
  end
26
31
  end
32
+
33
+ def check_job_sums(js_sum, j_sum)
34
+ js_sum[:num_jobs].should eql j_sum[:jobs].length
35
+ [:cpu_time, :memory_time, :successful].each do |field|
36
+ js_sum[field].should eql j_sum[field]
37
+ end
38
+ true
39
+ end
27
40
  end
28
41
 
29
42
  describe Bookie::Database do
@@ -99,6 +112,15 @@ describe Bookie::Database do
99
112
  end
100
113
 
101
114
  it "correctly filters by user" do
115
+ user = Bookie::Database::User.by_name('test').order(:id).first
116
+ jobs = @jobs.by_user(user).all
117
+ jobs.each do |job|
118
+ job.user.should eql user
119
+ end
120
+ jobs.length.should eql 10
121
+ end
122
+
123
+ it "correctly filters by user name" do
102
124
  jobs = @jobs.by_user_name('root').all
103
125
  jobs.length.should eql 10
104
126
  jobs[0].user.name.should eql "root"
@@ -112,7 +134,7 @@ describe Bookie::Database do
112
134
  jobs.length.should eql 0
113
135
  end
114
136
 
115
- it "correctly filters by group" do
137
+ it "correctly filters by group name" do
116
138
  jobs = @jobs.by_group_name("root").all
117
139
  jobs.length.should eql 10
118
140
  jobs.each do |job|
@@ -126,6 +148,15 @@ describe Bookie::Database do
126
148
  end
127
149
 
128
150
  it "correctly filters by system" do
151
+ sys = Bookie::Database::System.first
152
+ jobs = @jobs.by_system(sys)
153
+ jobs.length.should eql 10
154
+ jobs.each do |job|
155
+ job.system.should eql sys
156
+ end
157
+ end
158
+
159
+ it "correctly filters by system name" do
129
160
  jobs = @jobs.by_system_name('test1')
130
161
  jobs.length.should eql 20
131
162
  jobs = @jobs.by_system_name('test2')
@@ -153,43 +184,48 @@ describe Bookie::Database do
153
184
  end
154
185
 
155
186
  it "correctly filters by start time" do
156
- jobs = @jobs.by_start_time_range(@base_time, @base_time + 3600 * 2 + 1)
187
+ jobs = @jobs.by_start_time_range(@base_time ... @base_time + 3600 * 2 + 1)
157
188
  jobs.length.should eql 3
158
- jobs = @jobs.by_start_time_range(@base_time + 1, @base_time + 3600 * 2)
189
+ jobs = @jobs.by_start_time_range(@base_time + 1 ... @base_time + 3600 * 2)
159
190
  jobs.length.should eql 1
160
- jobs = @jobs.by_start_time_range(Time.at(0), Time.at(3))
191
+ jobs = @jobs.by_start_time_range(Time.at(0) ... Time.at(3))
161
192
  jobs.length.should eql 0
162
193
  end
163
194
 
164
195
  it "correctly filters by end time" do
165
- jobs = @jobs.by_end_time_range(@base_time, @base_time + 3600 * 2 + 1)
196
+ jobs = @jobs.by_end_time_range(@base_time ... @base_time + 3600 * 2 + 1)
166
197
  jobs.length.should eql 2
167
- jobs = @jobs.by_end_time_range(@base_time + 1, @base_time + 3600 * 2)
198
+ jobs = @jobs.by_end_time_range(@base_time + 1 ... @base_time + 3600 * 2)
168
199
  jobs.length.should eql 1
169
- jobs = @jobs.by_end_time_range(Time.at(0), Time.at(3))
200
+ jobs = @jobs.by_end_time_range(Time.at(0) ... Time.at(3))
170
201
  jobs.length.should eql 0
171
202
  end
172
203
 
173
- it "correctly filters by inclusive time range" do
174
- jobs = @jobs.by_time_range_inclusive(@base_time, @base_time + 3600 * 2 + 1)
175
- jobs.length.should eql 3
176
- jobs = @jobs.by_time_range_inclusive(@base_time + 1, @base_time + 3600 * 2 - 1)
177
- jobs.length.should eql 2
178
- jobs = @jobs.by_time_range_inclusive(Time.at(0), Time.at(3))
179
- jobs.length.should eql 0
180
- expect {
181
- @jobs.by_time_range_inclusive(Time.local(2012), Time.local(2012) - 1)
182
- }.to raise_error('Max time must be greater than or equal to min time')
204
+ describe "#by_time_range_inclusive" do
205
+ it "correctly filters by inclusive time range" do
206
+ jobs = @jobs.by_time_range_inclusive(@base_time ... @base_time + 3600 * 2 + 1)
207
+ jobs.length.should eql 3
208
+ jobs = @jobs.by_time_range_inclusive(@base_time + 1 ... @base_time + 3600 * 2 - 1)
209
+ jobs.length.should eql 2
210
+ jobs = @jobs.by_time_range_inclusive(Time.at(0) ... Time.at(3))
211
+ jobs.length.should eql 0
212
+ end
213
+
214
+ it "correctly handles inverted ranges" do
215
+ t = Date.new(2012).to_time
216
+ jobs = @jobs.by_time_range_inclusive(t ... t - 1)
217
+ jobs.count.should eql 0
218
+ end
183
219
  end
184
220
 
185
221
  it "correctly chains filters" do
186
222
  jobs = @jobs.by_user_name("test")
187
- jobs = jobs.by_start_time_range(@base_time + 3600, @base_time + 3601)
223
+ jobs = jobs.by_start_time_range(@base_time + 3600 ... @base_time + 3601)
188
224
  jobs.length.should eql 1
189
225
  jobs[0].user.group.name.should eql "default"
190
226
  end
191
227
 
192
- describe "::all_with_relations" do
228
+ describe "#all_with_relations" do
193
229
  it "loads all relations" do
194
230
  jobs = Bookie::Database::Job.limit(5)
195
231
  relations = {}
@@ -199,7 +235,7 @@ describe Bookie::Database do
199
235
  end
200
236
  end
201
237
 
202
- describe "::summary" do
238
+ describe "#summary" do
203
239
  before(:all) do
204
240
  Time.expects(:now).returns(Time.local(2012) + 36000 * 4).at_least_once
205
241
  @base_time = Time.local(2012)
@@ -210,51 +246,67 @@ describe Bookie::Database do
210
246
 
211
247
  it "produces correct summary totals" do
212
248
  @summary[:all][:jobs].length.should eql @length
213
- @summary[:all][:wall_time].should eql @length * 3600
214
249
  @summary[:all][:cpu_time].should eql @length * 100
215
250
  @summary[:all][:memory_time].should eql @length * 200 * 3600
216
- @summary[:all][:successful].should eql 0.5
251
+ @summary[:all][:successful].should eql 20
217
252
  @summary[:all_constrained][:jobs].length.should eql @length
218
- @summary[:all_constrained][:wall_time].should eql @length * 3600
219
253
  @summary[:all_constrained][:cpu_time].should eql @length * 100
220
- @summary[:all_constrained][:successful].should eql 0.5
254
+ @summary[:all_constrained][:successful].should eql 20
255
+ @summary[:all_filtered][:jobs].length.should eql @length / 2
256
+ @summary[:all_filtered][:cpu_time].should eql @length * 100 / 2
257
+ @summary[:all_filtered][:successful].should eql 20
221
258
  clipped_jobs = @summary[:clipped][:jobs].length
222
259
  clipped_jobs.should eql 25
223
- @summary[:clipped][:wall_time].should eql clipped_jobs * 3600 - 1800
224
260
  @summary[:clipped][:cpu_time].should eql clipped_jobs * 100 - 50
225
261
  @summary[:clipped][:memory_time].should eql clipped_jobs * 200 * 3600 - 100 * 3600
262
+ #Luckily for us, rounding down gives us the right answer. This is a bit fragile, though.
263
+ @summary[:clipped][:successful].should eql clipped_jobs / 2
226
264
  end
227
265
 
228
266
  it "correctly handles summaries of empty sets" do
229
267
  @summary[:empty].should eql({
230
268
  :jobs => [],
231
- :wall_time => 0,
232
269
  :cpu_time => 0,
233
270
  :memory_time => 0,
234
- :successful => 0.0,
271
+ :successful => 0,
235
272
  })
236
273
  end
237
274
 
238
- it "correctly handles jobs with zero wall time" do
275
+ it "correctly handles summaries with zero wall time" do
239
276
  job = @jobs.order(:start_time).first
240
277
  wall_time = job.wall_time
241
278
  begin
242
279
  job.wall_time = 0
243
280
  job.save!
244
- @jobs.order(:start_time).limit(1).summary[:wall_time].should eql 0
281
+ @jobs.order(:start_time).limit(1).summary[:cpu_time].should eql 0
245
282
  ensure
246
283
  job.wall_time = wall_time
247
284
  job.save!
248
285
  end
249
286
  end
250
287
 
251
- it "validates arguments" do
252
- expect {
253
- @jobs.summary(Time.local(2012), nil)
254
- }.to raise_error('Max time must be specified with min time')
255
- expect {
256
- @jobs.summary(Time.local(2012), Time.local(2012) - 1)
257
- }.to raise_error('Max time must be greater than or equal to min time')
288
+ it "only considers finished jobs to be successful" do
289
+ begin
290
+ job = Bookie::Database::Job.create!(
291
+ :user => Bookie::Database::User.first,
292
+ :system => Bookie::Database::System.first,
293
+ :command_name => '',
294
+ :cpu_time => 100,
295
+ :start_time => Time.local(2013),
296
+ :wall_time => 3600 * 24 * 2,
297
+ :memory => 10000,
298
+ :exit_code => 0
299
+ )
300
+ @jobs.summary(job.start_time ... job.start_time + 3600 * 24)[:successful].should eql 0
301
+ ensure
302
+ job.delete if job
303
+ end
304
+ end
305
+
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]
258
310
  end
259
311
  end
260
312
 
@@ -262,6 +314,7 @@ describe Bookie::Database do
262
314
  fields = {
263
315
  :user => Bookie::Database::User.first,
264
316
  :system => Bookie::Database::System.first,
317
+ :command_name => '',
265
318
  :cpu_time => 100,
266
319
  :start_time => Time.local(2012),
267
320
  :wall_time => 1000,
@@ -286,15 +339,310 @@ describe Bookie::Database do
286
339
  m.call(0)
287
340
  job.valid?.should eql true
288
341
  end
342
+ end
343
+ end
344
+
345
+ describe Bookie::Database::JobSummary do
346
+
347
+ describe "" do
348
+ before(:all) do
349
+ d = Date.new(2012)
350
+ Bookie::Database::User.all.each do |user|
351
+ Bookie::Database::System.all.each do |system|
352
+ ['vi', 'emacs'].each do |command_name|
353
+ (d ... d + 2).each do |date|
354
+ Bookie::Database::JobSummary.create!(
355
+ :user => user,
356
+ :system => system,
357
+ :command_name => command_name,
358
+ :date => date,
359
+ :num_jobs => 0,
360
+ :cpu_time => 0,
361
+ :memory_time => 0,
362
+ :successful => 0
363
+ )
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end
289
369
 
290
- job = Bookie::Database::Job.new(fields)
291
- job.start_time = 0
292
- job.valid?.should eql false
370
+ it "correctly filters by date" do
371
+ d = Date.new(2012)
372
+ sums = Bookie::Database::JobSummary.by_date(d).all
373
+ sums.length.should eql 32
374
+ sums.each do |sum|
375
+ sum.date.should eql d
376
+ end
377
+ end
378
+
379
+ it "correctly filters by user" do
380
+ u = Bookie::Database::User.first
381
+ sums = Bookie::Database::JobSummary.by_user(u).all
382
+ sums.length.should eql 16
383
+ sums.each do |sum|
384
+ sum.user.should eql u
385
+ end
386
+ end
387
+
388
+ it "correctly filters by user name" do
389
+ sums = Bookie::Database::JobSummary.by_user_name('test').all
390
+ sums.length.should eql 32
391
+ sums.each do |sum|
392
+ sum.user.name.should eql 'test'
393
+ end
394
+ end
395
+
396
+ it "correctly filters by group" do
397
+ g = Bookie::Database::Group.find_by_name('admin')
398
+ sums = Bookie::Database::JobSummary.by_group(g).all
399
+ sums.length.should eql 32
400
+ sums.each do |sum|
401
+ sum.user.group.should eql g
402
+ end
403
+ end
404
+
405
+ it "correctly filters by group name" do
406
+ sums = Bookie::Database::JobSummary.by_group_name('admin').all
407
+ sums.length.should eql 32
408
+ sums.each do |sum|
409
+ sum.user.group.name.should eql 'admin'
410
+ end
411
+ end
412
+
413
+ it "correctly filters by system" do
414
+ s = Bookie::Database::System.first
415
+ sums = Bookie::Database::JobSummary.by_system(s).all
416
+ sums.length.should eql 16
417
+ sums.each do |sum|
418
+ sum.system.should eql s
419
+ end
420
+ end
421
+
422
+ it "correctly filters by system name" do
423
+ sums = Bookie::Database::JobSummary.by_system_name('test1').all
424
+ sums.length.should eql 32
425
+ sums.each do |sum|
426
+ sum.system.name.should eql 'test1'
427
+ end
428
+ end
429
+
430
+ it "correctly filters by system type" do
431
+ s = Bookie::Database::SystemType.first
432
+ sums = Bookie::Database::JobSummary.by_system_type(s).all
433
+ sums.length.should eql 32
434
+ sums.each do |sum|
435
+ sum.system.system_type.should eql s
436
+ end
437
+ end
438
+
439
+ it "correctly filters by command name" do
440
+ sums = Bookie::Database::JobSummary.by_command_name('vi').all
441
+ sums.length.should eql 32
442
+ sums.each do |sum|
443
+ sum.command_name.should eql 'vi'
444
+ end
445
+ end
446
+ end
447
+
448
+ describe "#find_or_new" do
449
+ it "creates a summary if needed" do
450
+ Bookie::Database::JobSummary.delete_all
451
+ s = Bookie::Database::JobSummary.find_or_new(Date.new(2012), 1, 1, 'vi')
452
+ s.persisted?.should eql false
453
+ s.num_jobs = 0
454
+ s.cpu_time = 0
455
+ s.memory_time = 0
456
+ s.successful = 0
457
+ s.save!
458
+ end
459
+
460
+ it "uses the old summary if present" do
461
+ s = Bookie::Database::JobSummary.find_or_new(Date.new(2012), 1, 1, 'vi')
462
+ s.persisted?.should eql true
463
+ end
464
+ end
465
+
466
+ describe "#summarize" do
467
+ before(:each) do
468
+ Bookie::Database::JobSummary.delete_all
469
+ end
470
+
471
+ it "produces correct summaries" do
472
+ d = Date.new(2012)
473
+ range = d.to_time ... (d + 1).to_time
474
+ Bookie::Database::JobSummary.summarize(d)
475
+ sums = Bookie::Database::JobSummary.all
476
+ found_sums = Set.new
477
+ sums.each do |sum|
478
+ sum.date.should eql Date.new(2012)
479
+ jobs = Bookie::Database::Job.by_user(sum.user).by_system(sum.system).by_command_name(sum.command_name)
480
+ sum_2 = jobs.summary(range)
481
+ check_job_sums(sum, sum_2)
482
+ found_sums.add([sum.user.id, sum.system.id, sum.command_name])
483
+ end
484
+ #Is it producing all of the values needed?
485
+ Bookie::Database::Job.by_time_range_inclusive(range).select('user_id, system_id, command_name').uniq.all.each do |values|
486
+ values = [values.user_id, values.system_id, values.command_name]
487
+ found_sums.include?(values).should eql true
488
+ end
489
+ end
490
+ end
491
+
492
+ describe "#summary" do
493
+ before(:each) do
494
+ Bookie::Database::JobSummary.delete_all
495
+ t = (Date.new(2012) + 3).to_time
496
+ Time.expects(:now).at_least(0).returns(t)
497
+ end
498
+
499
+ #To do: test inclusive ranges?
500
+ it "produces correct summaries" do
501
+ #To consider: flesh out some more?
502
+ date_start = Date.new(2012)
503
+ date_end = date_start
504
+ date_bound = date_start + 3
505
+ while date_start < date_bound
506
+ while date_end < date_bound
507
+ range = date_start ... date_end
508
+ time_range = date_start.to_time ... date_end.to_time
509
+ sum1 = Bookie::Database::JobSummary.summary(:range => range)
510
+ sum2 = Bookie::Database::Job.summary(time_range)
511
+ check_job_sums(sum1, sum2)
512
+ Bookie::Database::JobSummary.summary(:range => time_range).should eql sum1
513
+ date_end += 1
514
+ end
515
+ date_start += 1
516
+ end
517
+ date_start = Date.new(2012)
518
+ time_start = date_start.to_time
519
+ time_end = (date_start + 1).to_time
520
+ [0, -7200, 7200].each do |offset_begin|
521
+ [0, -7200, 7200].each do |offset_end|
522
+ range_offset = time_start + offset_end ... time_end + offset_end
523
+ sum1 = Bookie::Database::JobSummary.summary(:range => range_offset)
524
+ sum2 = Bookie::Database::Job.summary(range_offset)
525
+ check_job_sums(sum1, sum2)
526
+ end
527
+ end
528
+ end
529
+
530
+ def check_time_bounds
531
+ date_min = Date.new(2012)
532
+ date_max = date_min + 1
533
+ check_job_sums(Bookie::Database::JobSummary.summary, Bookie::Database::Job.summary)
534
+ Bookie::Database::JobSummary.order(:date).first.date.should eql date_min
535
+ Bookie::Database::JobSummary.order('date DESC').first.date.should eql date_max
536
+ end
537
+
538
+ it "correctly finds the default time bounds" do
539
+ check_time_bounds
540
+ systems = Bookie::Database::System.active_systems
541
+ Bookie::Database::JobSummary.delete_all
542
+ #Check the case where all systems are decommissioned.
543
+ begin
544
+ systems.each do |sys|
545
+ sys.end_time = Time.now
546
+ sys.save!
547
+ end
548
+ check_time_bounds
549
+ ensure
550
+ systems.each do |sys|
551
+ sys.end_time = nil
552
+ sys.save!
553
+ end
554
+ end
555
+ Bookie::Database::JobSummary.delete_all
556
+ empty = Bookie::Database::System.limit(0)
557
+ #Check the case where there are no systems.
558
+ ActiveRecord::Relation.any_instance.expects(:'any?').at_least_once.returns(false)
559
+ ActiveRecord::Relation.any_instance.expects(:first).at_least_once.returns(nil)
560
+ sum = Bookie::Database::JobSummary.summary
561
+ sum.should eql({
562
+ :num_jobs => 0,
563
+ :cpu_time => 0,
564
+ :memory_time => 0,
565
+ :successful => 0,
566
+ })
567
+ ActiveRecord::Relation.any_instance.unstub(:'any?')
568
+ Bookie::Database::JobSummary.any?.should eql false
569
+ end
570
+
571
+ it "correctly handles filtered summaries" do
572
+ filters = {
573
+ :user_name => 'blm',
574
+ :group_name => 'blm',
575
+ :command_name => 'vi',
576
+ }
577
+ filters.each do |filter, value|
578
+ filter_sym = "by_#{filter}".intern
579
+ jobs = Bookie::Database::Job.send(filter_sym, value)
580
+ sum1 = Bookie::Database::JobSummary.send(filter_sym, value).summary(:jobs => jobs)
581
+ sum2 = jobs.summary
582
+ check_job_sums(sum1, sum2)
583
+ end
584
+ end
585
+
586
+ it "correctly handles inverted ranges" do
587
+ t = Date.new(2012).to_time
588
+ Bookie::Database::JobSummary.summary(:range => t .. t - 1).should eql({
589
+ :num_jobs => 0,
590
+ :cpu_time => 0,
591
+ :memory_time => 0,
592
+ :successful => 0,
593
+ })
594
+ end
595
+
596
+ it "caches summaries" do
597
+ Bookie::Database::JobSummary.summary
598
+ Bookie::Database::Job.expects(:summary).never
599
+ range = Date.new(2012) ... Date.new(2012) + 1
600
+ Bookie::Database::JobSummary.summary(:range => range)
601
+ end
602
+ end
603
+
604
+ it "validates fields" do
605
+ fields = {
606
+ :user => Bookie::Database::User.first,
607
+ :system => Bookie::Database::System.first,
608
+ :command_name => '',
609
+ :date => Date.new(2012),
610
+ :num_jobs => 1,
611
+ :cpu_time => 100,
612
+ :memory_time => 1000000,
613
+ :successful => 1,
614
+ }
615
+
616
+ sum = Bookie::Database::JobSummary.new(fields)
617
+ sum.valid?.should eql true
618
+
619
+ fields.each_key do |field|
620
+ job = Bookie::Database::JobSummary.new(fields)
621
+ job.method("#{field}=".intern).call(nil)
622
+ job.valid?.should eql false
623
+ end
624
+
625
+ [:cpu_time, :memory_time, :successful].each do |field|
626
+ job = Bookie::Database::JobSummary.new(fields)
627
+ m = job.method("#{field}=".intern)
628
+ m.call(-1)
629
+ job.valid?.should eql false
630
+ m.call(0)
631
+ job.valid?.should eql true
632
+ end
293
633
  end
294
634
  end
295
635
 
296
636
  describe Bookie::Database::User do
297
- describe "::find_or_create" do
637
+ it "correctly filters by name" do
638
+ users = Bookie::Database::User.by_name('test').all
639
+ users.length.should eql 2
640
+ users.each do |user|
641
+ user.name.should eql 'test'
642
+ end
643
+ end
644
+
645
+ describe "#find_or_create" do
298
646
  before(:each) do
299
647
  @group = Bookie::Database::Group.find_by_name('admin')
300
648
  end
@@ -343,7 +691,7 @@ describe Bookie::Database do
343
691
  end
344
692
 
345
693
  describe Bookie::Database::Group do
346
- describe "::find_or_create" do
694
+ describe "#find_or_create" do
347
695
  it "creates the group if needed" do
348
696
  Bookie::Database::Group.expects(:"create!")
349
697
  Bookie::Database::Group.find_or_create!('non_root')
@@ -394,13 +742,13 @@ describe Bookie::Database do
394
742
  end
395
743
  end
396
744
 
397
- describe "::summary" do
745
+ describe "#summary" do
398
746
  before(:all) do
399
747
  Time.expects(:now).returns(Time.local(2012) + 3600 * 40).at_least_once
400
748
  @base_time = Time.local(2012)
401
749
  @systems = Bookie::Database::System
402
750
  @summary = Helpers::create_summaries(@systems, Time.local(2012))
403
- @summary_wide = @systems.summary(Time.local(2012) - 3600, Time.local(2012) + 3600 * 40 + 3600)
751
+ @summary_wide = @systems.summary(Time.local(2012) - 3600 ... Time.local(2012) + 3600 * 40 + 3600)
404
752
  end
405
753
 
406
754
  it "produces correct summaries" do
@@ -437,7 +785,7 @@ describe Bookie::Database do
437
785
  end
438
786
  summary_all_systems_ended = @systems.summary()
439
787
  summary_all_systems_ended.should eql @summary[:all]
440
- summary_all_systems_ended = @systems.summary(Time.local(2012), Time.now + 3600)
788
+ summary_all_systems_ended = @systems.summary(Time.local(2012) ... Time.now + 3600)
441
789
  s2 = @summary[:all].dup
442
790
  s2[:avail_memory_avg] = Float(1000000 * system_total_wall_time) / (3600 * 41)
443
791
  summary_all_systems_ended.should eql s2
@@ -451,60 +799,63 @@ describe Bookie::Database do
451
799
  end
452
800
  end
453
801
 
454
- it "validates arguments" do
455
- expect {
456
- @systems.summary(Time.local(2012), nil)
457
- }.to raise_error('Max time must be specified with min time')
458
- expect {
459
- @systems.summary(Time.local(2012), Time.local(2012) - 1)
460
- }.to raise_error('Max time must be greater than or equal to min time')
802
+ it "correctly handles inverted ranges" do
803
+ t = Date.new(2012).to_time
804
+ @systems.summary(t ... t - 1).should eql @summary[:empty]
805
+ @systems.summary(t .. t - 1).should eql @summary[:empty]
461
806
  end
462
807
  end
463
808
 
464
- describe "::find_active" do
809
+ describe "#find_current" do
465
810
  before(:all) do
466
- @FIELDS = {
467
- :name => 'test',
468
- :start_time => Time.local(2012),
469
- :system_type => Bookie::Database::SystemType.first,
470
- :cores => 2,
471
- :memory => 1000000
472
- }
811
+ @config_t1 = @config.clone
812
+
813
+ @config_t1.hostname = 'test1'
814
+ @config_t1.system_type = 'standalone'
815
+ @config_t1.cores = 2
816
+ @config_t1.memory = 1000000
817
+
818
+ @config_t2 = @config_t1.clone
819
+ @config_t2.system_type = 'torque_cluster'
820
+
821
+ @sender_1 = Bookie::Sender.new(@config_t1)
822
+ @sender_2 = Bookie::Sender.new(@config_t2)
473
823
  end
474
-
475
- it "raises an error if no versions or only old versions of this system exist" do
476
- create_fields = @FIELDS.dup
477
- create_fields[:end_time] = Time.local(2012) + 1
478
- sys = Bookie::Database::System.create!(create_fields)
479
- begin
480
- expect {
481
- Bookie::Database::System.find_active(@FIELDS)
482
- }.to raise_error("There is no active system with hostname 'test' in the database.")
483
- ensure
484
- sys.delete
485
- end
824
+
825
+ it "finds the correct system" do
826
+ Bookie::Database::System.find_current(@sender_2).id.should eql 2
827
+ Bookie::Database::System.find_current(@sender_2, Time.now).id.should eql 2
828
+ Bookie::Database::System.find_current(@sender_1, Date.new(2012, 1, 1).to_time).id.should eql 1
486
829
  end
487
-
488
- it "finds the existing active system" do
489
- sys = Bookie::Database::System.create!(@FIELDS)
490
- begin
491
- Bookie::Database::System.find_active(@FIELDS).should eql sys
492
- ensure
493
- sys.delete
494
- end
830
+
831
+ it "correctly detects the lack of a matching system" do
832
+ expect {
833
+ Bookie::Database::System.find_current(@sender_1, Date.new(2011, 1, 1).to_time)
834
+ }.to raise_error(/^There is no system with hostname 'test1' in the database at /)
835
+ @config_t1.expects(:hostname).at_least_once.returns('test1000')
836
+ expect {
837
+ Bookie::Database::System.find_current(@sender_1, Date.new(2012, 1, 1).to_time)
838
+ }.to raise_error(/^There is no system with hostname 'test1000' in the database at /)
495
839
  end
496
840
 
497
841
  it "correctly detects conflicts" do
498
- fields = @FIELDS.dup
499
- fields[:cores] = 1
500
- csys = Bookie::Database::System.create!(fields)
501
- begin
842
+ config = @config.clone
843
+ config.hostname = 'test1'
844
+ config.cores = 2
845
+ config.memory = 1000000
846
+
847
+ sender = Bookie::Sender.new(config)
848
+ [:cores, :memory].each do |field|
849
+ config.expects(field).at_least_once.returns("value")
502
850
  expect {
503
- Bookie::Database::System.find_active(@FIELDS)
851
+ Bookie::Database::System.find_current(sender)
504
852
  }.to raise_error(Bookie::Database::System::SystemConflictError)
505
- ensure
506
- csys.delete
853
+ config.unstub(field)
507
854
  end
855
+ sender.expects(:system_type).returns(Bookie::Database::SystemType.find_by_name("Standalone"))
856
+ expect {
857
+ Bookie::Database::System.find_current(sender)
858
+ }.to raise_error(Bookie::Database::System::SystemConflictError)
508
859
  end
509
860
  end
510
861
 
@@ -549,10 +900,6 @@ describe Bookie::Database do
549
900
  system.valid?.should eql true
550
901
  end
551
902
 
552
- system = Bookie::Database::System.new(fields)
553
- system.start_time = 0
554
- system.valid?.should eql false
555
-
556
903
  system = Bookie::Database::System.new(fields)
557
904
  system.end_time = Time.local(2012)
558
905
  system.valid?.should eql true