priteau-vizir 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -9,6 +9,7 @@ begin
9
9
  gem.email = "pierre.riteau@gmail.com"
10
10
  gem.homepage = "http://github.com/priteau/vizir"
11
11
  gem.authors = ["Pierre Riteau"]
12
+ gem.rubyforge_project = "vizir"
12
13
  gem.add_dependency "eventmachine"
13
14
  gem.add_dependency "growlnotifier"
14
15
  gem.add_dependency "json"
@@ -17,6 +18,7 @@ begin
17
18
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
19
  end
19
20
 
21
+ Jeweler::RubyforgeTasks.new
20
22
  rescue LoadError
21
23
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
24
  end
@@ -24,7 +26,7 @@ end
24
26
  require 'rake/testtask'
25
27
  Rake::TestTask.new(:test) do |test|
26
28
  test.libs << 'lib' << 'test'
27
- test.pattern = 'test/**/*_test.rb'
29
+ test.pattern = 'test/**/test_*.rb'
28
30
  test.verbose = true
29
31
  end
30
32
 
@@ -32,7 +34,7 @@ begin
32
34
  require 'rcov/rcovtask'
33
35
  Rcov::RcovTask.new do |test|
34
36
  test.libs << 'test'
35
- test.pattern = 'test/**/*_test.rb'
37
+ test.pattern = 'test/**/test_*.rb'
36
38
  test.verbose = true
37
39
  end
38
40
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.2.3
data/bin/vizir CHANGED
@@ -4,43 +4,13 @@ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
4
4
 
5
5
  require 'eventmachine'
6
6
  require 'growl'
7
- require 'json'
8
7
  require 'net/ssh/gateway'
9
8
  require 'optparse'
10
9
  require 'rest_client'
11
10
  require 'vizir'
12
11
 
13
- FIRST_ALERT_TIME = 600
14
- ALERT_PERIOD = 60
15
- REFRESH_TIME = 3600
16
- IGNORE_SITES = ['grenoble-exp', 'grenoble-ext', 'grenoble-obs', 'luxembourg', 'portoalegre']
17
-
18
- def usage
19
- puts 'usage: vizir [ -h | --help ] [ -l | --login login_name ]'
20
- exit 1
21
- end
22
-
23
- def get(api, uri)
24
- begin
25
- return JSON.parse(api[uri + '?structure=simple'].get(:accept => 'application/json'))
26
- rescue RestClient::RequestTimeout => e
27
- # This should really be elsewhere, but will do for now
28
- # Were we querying a specific site? If yes, remove it from $sites
29
- if uri =~ /\/sites\/(\w+)/
30
- $sites.delete_if { |site| site['site'] == "#{$1}" }
31
- else
32
- raise e
33
- end
34
- rescue => e
35
- if e.respond_to?('http_code')
36
- puts "Error: #{e.http_code}:\n #{e.response.body}"
37
- return nil
38
- else
39
- puts e.inspect
40
- exit 1
41
- end
42
- end
43
- end
12
+ $alert_period = 60
13
+ $refresh_period = 3600
44
14
 
45
15
  def setup_ssh_tunnel
46
16
  gateway = Net::SSH::Gateway.new('access.grenoble.grid5000.fr', $login)
@@ -58,87 +28,47 @@ def setup_growl
58
28
  end
59
29
 
60
30
  def notify_via_growl(jobid, site_name, time, time_unit)
61
- $growl.notify('Job ending soon', 'OAR job terminating soon', "Job #{jobid} in #{site_name} ending in #{time} #{time_unit}.")
62
- end
63
-
64
- def get_remaining_time(time)
65
- return (time - Time.now).round
31
+ $growl.notify('Job ending soon', 'OAR job terminating soon', "Job #{jobid} in #{site_name} ending in #{time} #{time_unit}.", :sticky => true)
66
32
  end
67
33
 
68
34
  $login = nil
69
- opts = OptionParser.new
70
- opts.on('-h', '--help') { usage }
71
- opts.on('-l', '--login STRING', String) { |str| $login = str }
72
- opts.parse(ARGV)
73
-
74
- if $login == nil
75
- usage
76
- end
77
35
 
78
- gateway, port = setup_ssh_tunnel
79
- api = RestClient::Resource.new(apiuri(port))
80
- setup_growl
36
+ opts = OptionParser.new do |opts|
37
+ opts.banner = "Usage: vizir [options]"
38
+ opts.separator("")
81
39
 
82
- def learn_new_jobs(api)
83
- $sites.each do |site|
84
- site_name = site['site']
85
- unless IGNORE_SITES.include?(site_name)
86
- jobs = get(api, "#{site['uri']}/jobs")
87
- break if jobs == nil
88
- jobs.each do |job|
89
- if job['owner'] == $login
90
- job_details = get(api, "#{job['uri']}")
91
- break if job_details == nil
92
- if job_details['jobType'] == 'INTERACTIVE'
93
- jobid = job_details['Job_Id']
94
-
95
- # If we don't yet know the job, record it in $jobs
96
- if $jobs[jobid].nil?
97
- $jobs[jobid] = Vizir::Job.new(jobid, Time.at(Integer(job_details['startTime']) + Integer(job_details['walltime'])), site_name.capitalize)
98
- end
99
- end
100
- end
101
- end
102
- end
40
+ opts.separator("Options:")
41
+ opts.on('-a', '--alert-period TIME', Integer, "Job alert period (seconds), default: #{$alert_period}") { |t| $alert_period = t }
42
+ opts.on('-h', '--help', 'Print help') do
43
+ puts opts
44
+ exit
103
45
  end
46
+ opts.on('-l', '--login LOGIN', String, "User login on Grid\'5000") { |str| $login = str }
47
+ opts.on('-m', '--min-job-time TIME', Integer, "Minimum job time (seconds), default: #{$refresh_period}") { |t| $refresh_period = t }
104
48
  end
105
49
 
106
- def first_ending_job
107
- $jobs.sort{|a,b| a[1].end_time <=> b[1].end_time}.first
108
- end
50
+ opts.parse!(ARGV)
109
51
 
110
- def alert_jobs(api)
111
- $jobs.each do |jobid, job|
112
- remaining_sec = get_remaining_time(job.end_time)
113
- if remaining_sec < FIRST_ALERT_TIME
114
- # Check if the job still exists before sending a notification
115
- updatedjob = get(api, "/sites/#{job.site_name.downcase}/jobs/#{job.id}")
116
- if updatedjob['state'] != 'Running'
117
- # Job is not running anymore, remove it from the hash
118
- $jobs.delete(jobid)
119
- next
120
- end
121
- if remaining_sec < 0
122
- $stderr.puts "Error: negative time"
123
- next
124
- end
125
- remaining_time, remaining_time_unit = Vizir.humanize_time(remaining_sec)
126
- notify_via_growl(jobid, job.site_name, remaining_time, remaining_time_unit)
127
- end
128
- end
52
+ if $login == nil
53
+ puts opts
54
+ exit
129
55
  end
130
56
 
57
+ gateway, port = setup_ssh_tunnel
58
+ api = Vizir.get_api(apiuri(port))
59
+ setup_growl
60
+
131
61
  # Learn the Grid'5000 sites
132
62
  $jobs = Hash.new
133
- $sites = get(api, '/sites')
63
+ $sites = Vizir.learn_sites(api, Vizir::IGNORED_SITES)
134
64
  exit 1 if $sites == nil
135
65
 
136
- learn_new_jobs(api)
137
- alert_jobs(api)
66
+ Vizir.learn_new_jobs(api)
67
+ Vizir.alert_jobs(api)
138
68
 
139
69
  EventMachine::run {
140
- EventMachine::PeriodicTimer.new(REFRESH_TIME) { learn_new_jobs(api) }
141
- EventMachine::PeriodicTimer.new(ALERT_PERIOD) { alert_jobs(api) }
70
+ EventMachine::PeriodicTimer.new($refresh_period) { Vizir.learn_new_jobs(api) }
71
+ EventMachine::PeriodicTimer.new($alert_period) { Vizir.alert_jobs(api) }
142
72
  }
143
73
 
144
74
  gateway.shutdown!
data/lib/vizir.rb CHANGED
@@ -1,4 +1,11 @@
1
+ require 'json'
2
+ require 'restclient'
3
+
1
4
  module Vizir
5
+ APIURL = "https://localhost:8080/oargridapi"
6
+ IGNORED_SITES = ['grenoble-exp', 'grenoble-ext', 'grenoble-obs', 'luxembourg', 'portoalegre']
7
+ FIRST_ALERT_TIME = 600
8
+
2
9
  def Vizir.humanize_time(time)
3
10
  fail "Can't humanize negative period" if time < 0
4
11
 
@@ -17,6 +24,90 @@ module Vizir
17
24
  return remaining_time, remaining_time_unit
18
25
  end
19
26
 
27
+ def Vizir.get_remaining_time(end_time, now)
28
+ return (end_time - now).round
29
+ end
30
+
31
+ def Vizir.get(api, uri)
32
+ begin
33
+ return JSON.parse(api[uri + '?structure=simple'].get(:accept => 'application/json'))
34
+ rescue RestClient::RequestTimeout => e
35
+ # This should really be elsewhere, but will do for now
36
+ # Were we querying a specific site? If yes, remove it from $sites
37
+ if uri =~ /\/sites\/(\w+)/
38
+ $sites.delete_if { |site| site['site'] == "#{$1}" }
39
+ return nil
40
+ else
41
+ raise e
42
+ end
43
+ rescue => e
44
+ if e.respond_to?('http_code')
45
+ puts "Error: #{e.http_code}:\n #{e.response.body}"
46
+ return nil
47
+ else
48
+ puts e.inspect
49
+ exit 1
50
+ end
51
+ end
52
+ end
53
+
54
+ def Vizir.learn_sites(api, ignored_sites)
55
+ return get(api, '/sites').delete_if { |hash| ignored_sites.include?(hash["site"]) }
56
+ end
57
+
58
+ def Vizir.get_api(uri)
59
+ return RestClient::Resource.new(uri, :timeout => 15)
60
+ end
61
+
62
+ def Vizir.learn_new_jobs_on_site(api, site)
63
+ site_name = site['site']
64
+ jobs = Vizir.get(api, "#{site['uri']}/jobs")
65
+ return if jobs.nil?
66
+ jobs.each do |job|
67
+ if job['owner'] == $login
68
+ job_details = Vizir.get(api, "#{job['uri']}")
69
+ break if job_details == nil
70
+ if job_details['jobType'] == 'INTERACTIVE'
71
+ jobid = job_details['Job_Id']
72
+ # If we don't yet know the job, record it in $jobs
73
+ if $jobs[jobid].nil?
74
+ $jobs[jobid] = Vizir::Job.new(jobid, Time.at(Integer(job_details['startTime']) + Integer(job_details['walltime'])), site_name.capitalize)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def Vizir.learn_new_jobs(api)
82
+ $sites.each do |site|
83
+ Vizir.learn_new_jobs_on_site(api, site)
84
+ end
85
+ end
86
+
87
+ def Vizir.first_ending_job
88
+ $jobs.sort{|a,b| a[1].end_time <=> b[1].end_time}.first
89
+ end
90
+
91
+ def Vizir.alert_jobs(api)
92
+ $jobs.each do |jobid, job|
93
+ if job.should_be_ending?
94
+ # Check if the job still exists before sending a notification
95
+ if job.is_ended?(api)
96
+ # Job is not running anymore, remove it from the hash
97
+ $jobs.delete(jobid)
98
+ next
99
+ end
100
+ remaining_sec = Vizir.get_remaining_time(job.end_time, Time.now)
101
+ if remaining_sec < 0
102
+ $stderr.puts "Error: negative time"
103
+ next
104
+ end
105
+ remaining_time, remaining_time_unit = Vizir.humanize_time(remaining_sec)
106
+ notify_via_growl(jobid, job.site_name, remaining_time, remaining_time_unit)
107
+ end
108
+ end
109
+ end
110
+
20
111
  class Job
21
112
  attr_reader :id, :end_time, :site_name
22
113
 
@@ -25,5 +116,23 @@ module Vizir
25
116
  @end_time = end_time
26
117
  @site_name = site_name
27
118
  end
119
+
120
+ def Job.create_from_json(json, site_name)
121
+ return Job.new(json['Job_Id'], Time.at(Integer(json['startTime']) + Integer(json['walltime'])), site_name.capitalize)
122
+ end
123
+
124
+ def fetch_details(api)
125
+ return Vizir.get(api, "/sites/#{@site_name.downcase}/jobs/#{@id}")
126
+ end
127
+
128
+ def should_be_ending?(time=Time.now())
129
+ return Vizir.get_remaining_time(self.end_time, time) <= Vizir::FIRST_ALERT_TIME
130
+ end
131
+
132
+ def is_ended?(api)
133
+ updatedjob = self.fetch_details(api)
134
+ return updatedjob['state'] != 'Running'
135
+ end
136
+
28
137
  end
29
138
  end
data/test/test_helper.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ require 'fakeweb'
3
6
 
4
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
9
  require 'vizir'
7
10
 
8
11
  class Test::Unit::TestCase
@@ -0,0 +1,99 @@
1
+ require 'test_helper'
2
+
3
+ class VizirTest < Test::Unit::TestCase
4
+ TIMES = [
5
+ [0, [0, "second"]],
6
+ [1, [1, "second"]],
7
+ [30, [30, "seconds"]],
8
+ [59, [59, "seconds"]],
9
+ [60, [1, "minute"]],
10
+ [119, [1, "minute"]],
11
+ [120, [2, "minutes"]]
12
+ ]
13
+ def test_humanize_time
14
+ TIMES.each do |seconds, result|
15
+ assert_equal(result, Vizir.humanize_time(seconds))
16
+ end
17
+ e = assert_raise(RuntimeError) { Vizir.humanize_time(-1) }
18
+ assert_equal("Can't humanize negative period", e.message)
19
+ end
20
+
21
+ def test_learn_sites
22
+ FakeWeb.allow_net_connect = false
23
+ o = [
24
+ {
25
+ "site" => "nancy",
26
+ "uri" => "/sites/nancy"
27
+ },
28
+ {
29
+ "site" => "rennes",
30
+ "uri" => "/sites/rennes"
31
+ },
32
+ {
33
+ "site" => "luxembourg",
34
+ "uri" => "/sites/luxembourg"
35
+ }]
36
+ json = JSON.generate(o)
37
+ FakeWeb.register_uri(:get, "#{Vizir::APIURL}/sites?structure=simple", :string => json)
38
+ api = Vizir.get_api(Vizir::APIURL)
39
+ sites = o.delete_if { |hash| hash["site"] == "luxembourg" }
40
+ ignored_sites = ["luxembourg", "portoalegre"]
41
+ assert_equal(sites, Vizir.learn_sites(api, ignored_sites))
42
+ end
43
+
44
+ context "A Job instance" do
45
+ setup do
46
+ @job_endtime = Time.at(123456789)
47
+ @job = Vizir::Job.new('1234', @job_endtime, 'Rennes')
48
+ end
49
+
50
+ context 'ending in FIRST_ALERT_TIME seconds' do
51
+ setup do
52
+ @now = Time.at(Integer(@job_endtime) - Integer(Vizir::FIRST_ALERT_TIME))
53
+ end
54
+
55
+ should 'be considered as ending soon' do
56
+ assert_equal true, @job.should_be_ending?(@now)
57
+ end
58
+ end
59
+
60
+ context 'ending in more than FIRST_ALERT_TIME seconds' do
61
+ setup do
62
+ @now = Time.at(Integer(@job_endtime) - Integer(Vizir::FIRST_ALERT_TIME + 1))
63
+ end
64
+
65
+ should 'not be considered as ending soon' do
66
+ assert_equal false, @job.should_be_ending?(@now)
67
+ end
68
+ end
69
+ end
70
+
71
+ context 'A site-specific query' do
72
+ setup do
73
+ @uri = "#{Vizir::APIURL}/sites/rennes/jobs?structure=simple"
74
+ end
75
+
76
+ context 'which timeouts' do
77
+ setup do
78
+ FakeWeb.register_uri(:get, @uri, :exception => Timeout::Error)
79
+ @api = Vizir.get_api(Vizir::APIURL)
80
+ end
81
+
82
+ should 'remove the site from the sites list' do
83
+ $sites = [
84
+ {
85
+ "site" => "nancy",
86
+ "uri" => "/sites/nancy"
87
+ },
88
+ {
89
+ "site" => "rennes",
90
+ "uri" => "/sites/rennes"
91
+ }
92
+ ]
93
+ Vizir.learn_new_jobs_on_site(@api, $sites[1])
94
+ assert_equal [{ "site" => "nancy", "uri" => "/sites/nancy" }], $sites
95
+ end
96
+ end
97
+ end
98
+
99
+ end
data/vizir.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{vizir}
5
- s.version = "0.2.2"
5
+ s.version = "0.2.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Pierre Riteau"]
9
- s.date = %q{2009-06-02}
9
+ s.date = %q{2009-06-18}
10
10
  s.default_executable = %q{vizir}
11
11
  s.email = %q{pierre.riteau@gmail.com}
12
12
  s.executables = ["vizir"]
@@ -24,18 +24,19 @@ Gem::Specification.new do |s|
24
24
  "bin/vizir",
25
25
  "lib/vizir.rb",
26
26
  "test/test_helper.rb",
27
- "test/vizir_test.rb",
27
+ "test/test_vizir.rb",
28
28
  "vizir.gemspec"
29
29
  ]
30
30
  s.has_rdoc = true
31
31
  s.homepage = %q{http://github.com/priteau/vizir}
32
32
  s.rdoc_options = ["--charset=UTF-8"]
33
33
  s.require_paths = ["lib"]
34
+ s.rubyforge_project = %q{vizir}
34
35
  s.rubygems_version = %q{1.3.1}
35
36
  s.summary = %q{Growl notifications for Grid'5000 jobs}
36
37
  s.test_files = [
37
38
  "test/test_helper.rb",
38
- "test/vizir_test.rb"
39
+ "test/test_vizir.rb"
39
40
  ]
40
41
 
41
42
  if s.respond_to? :specification_version then
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: priteau-vizir
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Riteau
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-02 00:00:00 -07:00
12
+ date: 2009-06-18 00:00:00 -07:00
13
13
  default_executable: vizir
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -81,7 +81,7 @@ files:
81
81
  - bin/vizir
82
82
  - lib/vizir.rb
83
83
  - test/test_helper.rb
84
- - test/vizir_test.rb
84
+ - test/test_vizir.rb
85
85
  - vizir.gemspec
86
86
  has_rdoc: true
87
87
  homepage: http://github.com/priteau/vizir
@@ -104,11 +104,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  version:
105
105
  requirements: []
106
106
 
107
- rubyforge_project:
107
+ rubyforge_project: vizir
108
108
  rubygems_version: 1.2.0
109
109
  signing_key:
110
110
  specification_version: 2
111
111
  summary: Growl notifications for Grid'5000 jobs
112
112
  test_files:
113
113
  - test/test_helper.rb
114
- - test/vizir_test.rb
114
+ - test/test_vizir.rb
data/test/vizir_test.rb DELETED
@@ -1,17 +0,0 @@
1
- require 'test_helper'
2
-
3
- class VizirTest < Test::Unit::TestCase
4
- TIMES = [
5
- [1, [1, "second"]],
6
- [30, [30, "seconds"]],
7
- [59, [59, "seconds"]],
8
- [60, [1, "minute"]],
9
- [120, [2, "minutes"]]
10
- ]
11
- def test_humanize_time
12
- TIMES.each do |seconds, result|
13
- assert_equal(result, Vizir.humanize_time(seconds))
14
- end
15
- assert_raise(RuntimeError) { Vizir.humanize_time(-1) }
16
- end
17
- end