torque-vpc-toolkit 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/.rvmrc +1 -0
- data/COPYING +26 -0
- data/README.rdoc +32 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/bin/torque-vpc-toolkit +28 -0
- data/contrib/conf/jobs.json.example +13 -0
- data/contrib/rake/Rakefile +16 -0
- data/lib/torque-vpc-toolkit.rb +277 -0
- data/rake/torque_vpc_toolkit.rake +197 -0
- metadata +105 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7@chef_vpc_toolkit
|
data/COPYING
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Unless otherwise noted, all files are released under the MIT license,
|
2
|
+
exceptions contain licensing information in them.
|
3
|
+
|
4
|
+
Copyright (C) 2010 Rackspace US, Inc.
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
23
|
+
|
24
|
+
Except as contained in this notice, the name of Rackspace US, Inc. shall not
|
25
|
+
be used in advertising or otherwise to promote the sale, use or other dealings
|
26
|
+
in this Software without prior written authorization from Rackspace US, Inc.
|
data/README.rdoc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
= Torque VPC Toolkit
|
2
|
+
|
3
|
+
Rake tasks to submit jobs and interact with Torque via a web API.
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
The Torque VPC Toolkit is a set of Rake tasks that provide a way to interact
|
8
|
+
with a Torque web API application. Intended to be used with the Chef VPC Toolkit and the Torque Job Control application.
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
gem install torque-vpc-toolkit
|
13
|
+
|
14
|
+
Once you have the Torque toolkit gem install you can add tasks to job tasks
|
15
|
+
to any Chef VPC by running the the following command inside of the Chef
|
16
|
+
VPC project directory.
|
17
|
+
|
18
|
+
cd <my chef vpc project>
|
19
|
+
torque-vpc-toolkit
|
20
|
+
|
21
|
+
== Tasks
|
22
|
+
|
23
|
+
The following Rake tasks are provided by the toolkit:
|
24
|
+
|
25
|
+
rake job:create_and_run_all # Create a new server group and run all jobs.
|
26
|
+
rake job:list # List jobs
|
27
|
+
rake job:log # Print job logs for the specified JOB_ID.
|
28
|
+
rake job:node_states # List node states
|
29
|
+
rake job:poll_controller # Poll/loop until job controller is online
|
30
|
+
rake job:poll_jobs # Poll/loop until jobs finish
|
31
|
+
rake job:submit # Submit a job (requires: SCRIPT=<job_script_name>)
|
32
|
+
rake job:submit_all # Submit all jobs (uses the jobs.json config file)
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "torque-vpc-toolkit"
|
8
|
+
gem.summary = %Q{Rake tasks to submit Torque jobs. }
|
9
|
+
gem.description = %Q{Rake tasks to submit, and poll Torque jobs.}
|
10
|
+
gem.email = "dan.prince@rackspace.com"
|
11
|
+
gem.homepage = "http://github.com/dprince/torque_vpc_toolkit"
|
12
|
+
gem.authors = ["Dan Prince"]
|
13
|
+
gem.add_dependency "rake", ">= 0"
|
14
|
+
gem.add_dependency "chef-vpc-toolkit", ">= 2.0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/rdoctask'
|
23
|
+
Rake::RDocTask.new do |rdoc|
|
24
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
25
|
+
|
26
|
+
rdoc.rdoc_dir = 'rdoc'
|
27
|
+
rdoc.title = "Torque VPC Toolkit #{version}"
|
28
|
+
rdoc.rdoc_files.include('README*')
|
29
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
30
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
CHEF_VPC_PROJECT = "#{File.dirname(__FILE__)}" unless defined?(CHEF_VPC_PROJECT)
|
7
|
+
|
8
|
+
require 'torque-vpc-toolkit'
|
9
|
+
include TorqueVPCToolkit
|
10
|
+
|
11
|
+
# check to make sure this is a valid Chef VPC project dir
|
12
|
+
if not File.exists?("config/chef_installer.yml") then
|
13
|
+
puts "Run this command within your Chef VPC project directory."
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
if not File.exists?("tasks") then
|
18
|
+
FileUtils.mkdir("tasks")
|
19
|
+
end
|
20
|
+
FileUtils.cp(File.join(TorqueVPCToolkit::TORQUE_VPC_TOOLKIT_ROOT, 'contrib', 'rake', 'Rakefile'), File.join("tasks", "torque_vpc_toolkit.rake"))
|
21
|
+
|
22
|
+
if not File.exists?("config/jobs.json") and not File.exists?(File.join('config', 'jobs.json.example')) then
|
23
|
+
FileUtils.cp(File.join(TorqueVPCToolkit::TORQUE_VPC_TOOLKIT_ROOT, 'contrib', 'conf', 'jobs.json.example'), 'config')
|
24
|
+
end
|
25
|
+
|
26
|
+
if not File.exists?("jobs") then
|
27
|
+
FileUtils.mkdir("jobs")
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
jc_toolkit_version=nil
|
4
|
+
if ENV['TORQUE_VPC_TOOLKIT_VERSION'] then
|
5
|
+
jc_toolkit_version=ENV['TORQUE_VPC_TOOLKIT_VERSION']
|
6
|
+
end
|
7
|
+
|
8
|
+
gem 'torque-vpc-toolkit', "= #{jc_toolkit_version}" if jc_toolkit_version
|
9
|
+
|
10
|
+
require 'torque-vpc-toolkit'
|
11
|
+
|
12
|
+
include TorqueVPCToolkit
|
13
|
+
|
14
|
+
Dir[File.join(TorqueVPCToolkit::TORQUE_VPC_TOOLKIT_ROOT, 'rake', '*.rake')].each do |rakefile|
|
15
|
+
import(rakefile)
|
16
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'rexml/xpath'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'chef-vpc-toolkit'
|
6
|
+
|
7
|
+
module TorqueVPCToolkit
|
8
|
+
|
9
|
+
TORQUE_VPC_TOOLKIT_ROOT = File.dirname(File.expand_path("./", File.dirname(__FILE__)))
|
10
|
+
|
11
|
+
include ChefVPCToolkit
|
12
|
+
|
13
|
+
def self.jobs_list(xml)
|
14
|
+
list=[]
|
15
|
+
dom = REXML::Document.new(xml)
|
16
|
+
|
17
|
+
REXML::XPath.each(dom, "//job") do |job|
|
18
|
+
job_attrs = {
|
19
|
+
"id" => job.elements["id"].text,
|
20
|
+
"description" => job.elements["description"].text,
|
21
|
+
"queue-job-id" => job.elements["queue-job-id"].text,
|
22
|
+
"resources" => job.elements["resources"].text,
|
23
|
+
"additional-attrs" => job.elements["additional-attrs"].text,
|
24
|
+
"status" => job.elements["status"].text
|
25
|
+
}
|
26
|
+
|
27
|
+
stdout=job.elements["stdout"]
|
28
|
+
job_attrs.store("stdout", stdout.text) if stdout
|
29
|
+
|
30
|
+
stderr=job.elements["stderr"]
|
31
|
+
job_attrs.store("stderr", stderr.text) if stderr
|
32
|
+
|
33
|
+
list << job_attrs
|
34
|
+
end
|
35
|
+
|
36
|
+
list
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.submit_job(configs, script, description, resources, additional_attrs="")
|
41
|
+
|
42
|
+
Util.raise_if_nil_or_empty(configs, "ssh_gateway_ip")
|
43
|
+
Util.raise_if_nil_or_empty(configs, "torque_job_control_username")
|
44
|
+
Util.raise_if_nil_or_empty(configs, "torque_job_control_password")
|
45
|
+
|
46
|
+
post_data={
|
47
|
+
"job[description]" => description
|
48
|
+
}
|
49
|
+
if not resources.nil? and not resources.empty? then
|
50
|
+
post_data.store("job[resources]", resources)
|
51
|
+
end
|
52
|
+
if not additional_attrs.nil? and not additional_attrs.empty? then
|
53
|
+
post_data.store("job[additional_attrs]", additional_attrs)
|
54
|
+
end
|
55
|
+
|
56
|
+
file_data={
|
57
|
+
"job[script_file_upload]" => script
|
58
|
+
}
|
59
|
+
|
60
|
+
resp=HttpUtil.file_upload(
|
61
|
+
"https://"+configs["ssh_gateway_ip"]+"/jobs.xml",
|
62
|
+
file_data,
|
63
|
+
post_data,
|
64
|
+
configs["torque_job_control_username"],
|
65
|
+
configs["torque_job_control_password"]
|
66
|
+
)
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.node_states(xml)
|
71
|
+
|
72
|
+
node_states={}
|
73
|
+
dom = REXML::Document.new(xml)
|
74
|
+
|
75
|
+
REXML::XPath.each(dom, "//Node") do |job|
|
76
|
+
node_states.store(job.elements["name"].text, job.elements["state"].text)
|
77
|
+
end
|
78
|
+
|
79
|
+
node_states
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
# default timeout of 20 minutes
|
84
|
+
def self.poll_until_online(ip, timeout=1200, configs=Util.load_configs)
|
85
|
+
|
86
|
+
online = false
|
87
|
+
count=0
|
88
|
+
until online or (count*20) >= timeout.to_i do
|
89
|
+
count+=1
|
90
|
+
xml=""
|
91
|
+
begin
|
92
|
+
xml=HttpUtil.get(
|
93
|
+
"https://#{ip}/nodes",
|
94
|
+
configs["torque_job_control_username"],
|
95
|
+
configs["torque_job_control_password"]
|
96
|
+
)
|
97
|
+
rescue
|
98
|
+
sleep 20
|
99
|
+
next
|
100
|
+
end
|
101
|
+
|
102
|
+
jobs=TorqueVPCToolkit.node_states(xml)
|
103
|
+
|
104
|
+
online=true
|
105
|
+
jobs.each_pair do |name, state|
|
106
|
+
if state != "free" then
|
107
|
+
online=false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
if not online
|
111
|
+
yield jobs if block_given?
|
112
|
+
sleep 20
|
113
|
+
end
|
114
|
+
end
|
115
|
+
if (count*20) >= timeout.to_i then
|
116
|
+
raise "Timeout waiting for job control to come online."
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.print_job(hash)
|
122
|
+
|
123
|
+
puts "Job ID: #{hash["id"]}"
|
124
|
+
puts "description: #{hash["description"]}"
|
125
|
+
puts "Queue job ID: #{hash["queue-job-id"]}"
|
126
|
+
puts "Resources: #{hash["resources"]}"
|
127
|
+
puts "Additional Attrs: #{hash["additional-attrs"]}"
|
128
|
+
puts "Status: #{hash["status"]}"
|
129
|
+
puts "--"
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.submit_all(configs, config_file=CHEF_VPC_PROJECT + File::SEPARATOR + "config" + File::SEPARATOR + "jobs.json")
|
134
|
+
|
135
|
+
if not File.exists?(config_file) then
|
136
|
+
puts "The jobs.json config file is missing. No jobs scheduled."
|
137
|
+
return
|
138
|
+
end
|
139
|
+
|
140
|
+
json_hash=JSON.parse(IO.read(config_file))
|
141
|
+
|
142
|
+
# hash for job_name/job_id's (used for variable substitution)
|
143
|
+
jobid_vars={}
|
144
|
+
jobs_dir=CHEF_VPC_PROJECT + File::SEPARATOR + "jobs" + File::SEPARATOR
|
145
|
+
|
146
|
+
json_hash.each do |job|
|
147
|
+
script=job["script"]
|
148
|
+
name=job["name"]
|
149
|
+
if File.exists?(jobs_dir+script) then
|
150
|
+
resources=self.replace_jobid_vars(job["resources"], jobid_vars)
|
151
|
+
additional_attrs=self.replace_jobid_vars(job["additional_attrs"], jobid_vars)
|
152
|
+
xml=self.submit_job(configs, jobs_dir+script, name, resources, additional_attrs)
|
153
|
+
job_hash=TorqueVPCToolkit.jobs_list(xml)[0]
|
154
|
+
if jobid_vars.has_key?(name) then
|
155
|
+
raise "A unique job name must be specified in jobs.json"
|
156
|
+
else
|
157
|
+
jobid_vars.store(name, job_hash["queue-job-id"])
|
158
|
+
end
|
159
|
+
puts "\tJob ID "+job_hash["id"]+ " submitted."
|
160
|
+
|
161
|
+
else
|
162
|
+
raise "Job script '#{script}' does not exist."
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.job_hash(vpn_gateway, job_id, configs=Util.load_configs)
|
169
|
+
if job_id.nil? or job_id.empty? then
|
170
|
+
raise "A valid job_id is required."
|
171
|
+
end
|
172
|
+
xml=HttpUtil.get(
|
173
|
+
"https://#{vpn_gateway}/jobs/#{job_id}.xml",
|
174
|
+
configs["torque_job_control_username"],
|
175
|
+
configs["torque_job_control_password"]
|
176
|
+
)
|
177
|
+
TorqueVPCToolkit.jobs_list(xml)[0]
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.get_jobs(ip, configs)
|
182
|
+
xml=HttpUtil.get(
|
183
|
+
"https://#{ip}/jobs.xml",
|
184
|
+
configs["torque_job_control_username"],
|
185
|
+
configs["torque_job_control_password"]
|
186
|
+
)
|
187
|
+
return TorqueVPCToolkit.jobs_list(xml)
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.poll_until_job_range_finished(ip, from_id, to_id, timeout=1200, configs=Util.load_configs)
|
191
|
+
|
192
|
+
def gen_filter(from_id, to_id)
|
193
|
+
return Proc.new { |i| from_id <= i and i <= to_id }
|
194
|
+
end
|
195
|
+
|
196
|
+
criteria = gen_filter(from_id, to_id)
|
197
|
+
poll_until_jobs_finished(ip, timeout, configs, criteria)
|
198
|
+
end
|
199
|
+
|
200
|
+
# default timeout of 20 minutes
|
201
|
+
def self.poll_until_jobs_finished(ip, timeout=1200, configs=Util.load_configs, criteria=nil)
|
202
|
+
count=0
|
203
|
+
until (count*20) >= timeout.to_i do
|
204
|
+
count+=1
|
205
|
+
jobs = nil
|
206
|
+
begin
|
207
|
+
jobs=TorqueVPCToolkit.get_jobs(ip, configs)
|
208
|
+
rescue
|
209
|
+
sleep 20
|
210
|
+
next
|
211
|
+
end
|
212
|
+
|
213
|
+
all_jobs_finished = true
|
214
|
+
jobs.each do |job|
|
215
|
+
id = Integer(job['id'])
|
216
|
+
if criteria != nil and not criteria.call(id) then
|
217
|
+
next
|
218
|
+
end
|
219
|
+
|
220
|
+
if job["status"] == "Failed" then
|
221
|
+
raise "Job ID #{job['id']} failed."
|
222
|
+
elsif job["status"] != "Completed" then
|
223
|
+
all_jobs_finished = false
|
224
|
+
end
|
225
|
+
end
|
226
|
+
if all_jobs_finished then
|
227
|
+
break
|
228
|
+
else
|
229
|
+
yield jobs if block_given?
|
230
|
+
sleep 20
|
231
|
+
end
|
232
|
+
end
|
233
|
+
if (count*20) >= timeout.to_i then
|
234
|
+
raise "Timeout waiting for jobs to finish."
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
# parse the torque_server role for job_control credentials
|
240
|
+
def self.job_control_credentials(ip_addr)
|
241
|
+
role_text=%x{ssh root@#{ip_addr} /usr/bin/knife role show torque_server}
|
242
|
+
json=JSON.parse(role_text.gsub(/\"json_class\"[^,]*,/, ''))
|
243
|
+
username=json["override_attributes"]["job_control"]["auth_username"]
|
244
|
+
password=json["override_attributes"]["job_control"]["auth_password"]
|
245
|
+
if block_given?
|
246
|
+
yield username, password
|
247
|
+
else
|
248
|
+
{
|
249
|
+
"torque_job_control_username" => username,
|
250
|
+
"torque_job_control_password"=> password
|
251
|
+
}
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
private
|
256
|
+
def self.replace_jobid_vars(str, vars)
|
257
|
+
return nil if str.nil?
|
258
|
+
vars=vars.sort { |a,b| b[0].length <=> a[0].length }
|
259
|
+
vars.each do |arr|
|
260
|
+
regex=Regexp.new("\\$#{arr[0]}")
|
261
|
+
str=str.gsub(regex, arr[1])
|
262
|
+
end
|
263
|
+
str
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
def self.get_max_job_id(configs, hash)
|
268
|
+
ip=hash['vpn-gateway']
|
269
|
+
jobs=TorqueVPCToolkit.get_jobs(ip, configs)
|
270
|
+
|
271
|
+
if jobs.empty?
|
272
|
+
return 0
|
273
|
+
else
|
274
|
+
return jobs.collect { |job| Integer(job['id']) }.sort.last
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
namespace :job do
|
2
|
+
|
3
|
+
desc "Submit a job (requires: SCRIPT=<job_script_name>)"
|
4
|
+
task :submit do
|
5
|
+
|
6
|
+
script=ENV['SCRIPT']
|
7
|
+
resources=ENV['RESOURCES']
|
8
|
+
additional_attrs=ENV['ATTRIBUTES']
|
9
|
+
|
10
|
+
configs=Util.load_configs
|
11
|
+
hash=Util.hash_for_group(configs)
|
12
|
+
configs["ssh_gateway_ip"]=hash["vpn-gateway"]
|
13
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
14
|
+
|
15
|
+
xml=TorqueVPCToolkit.submit_job(configs, "jobs/#{script}", script, resources, additional_attrs)
|
16
|
+
job_hash=TorqueVPCToolkit.jobs_list(xml)[0]
|
17
|
+
TorqueVPCToolkit.print_job(job_hash)
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Submit all jobs (uses the jobs.json config file)"
|
22
|
+
task :submit_all do
|
23
|
+
|
24
|
+
configs=Util.load_configs
|
25
|
+
hash=Util.hash_for_group(configs)
|
26
|
+
configs["ssh_gateway_ip"]=hash["vpn-gateway"]
|
27
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
28
|
+
xml=TorqueVPCToolkit.submit_all(configs)
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Submit job group (requires: JOB_GROUP=<file>)"
|
33
|
+
task :submit_group do
|
34
|
+
job_group=ENV['JOB_GROUP']
|
35
|
+
|
36
|
+
configs=Util.load_configs
|
37
|
+
hash=Util.hash_for_group(configs)
|
38
|
+
configs["ssh_gateway_ip"]=hash["vpn-gateway"]
|
39
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
40
|
+
|
41
|
+
xml=TorqueVPCToolkit.submit_all(configs, job_group)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "List jobs"
|
45
|
+
task :list do
|
46
|
+
|
47
|
+
configs=Util.load_configs
|
48
|
+
hash=Util.hash_for_group(configs)
|
49
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
50
|
+
xml=HttpUtil.get(
|
51
|
+
"https://"+hash["vpn-gateway"]+"/jobs.xml",
|
52
|
+
configs["torque_job_control_username"],
|
53
|
+
configs["torque_job_control_password"]
|
54
|
+
)
|
55
|
+
jobs=TorqueVPCToolkit.jobs_list(xml)
|
56
|
+
puts "Jobs:"
|
57
|
+
jobs.each do |job|
|
58
|
+
puts "\t#{job['id']}: #{job['description']} (#{job['status']})"
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "List node states"
|
64
|
+
task :node_states do
|
65
|
+
|
66
|
+
configs=Util.load_configs
|
67
|
+
hash=Util.hash_for_group(configs)
|
68
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
69
|
+
xml=HttpUtil.get(
|
70
|
+
"https://"+hash["vpn-gateway"]+"/nodes",
|
71
|
+
configs["torque_job_control_username"],
|
72
|
+
configs["torque_job_control_password"]
|
73
|
+
)
|
74
|
+
node_states=TorqueVPCToolkit.node_states(xml)
|
75
|
+
puts "Nodes:"
|
76
|
+
node_states.each_pair do |name, state|
|
77
|
+
puts "\t#{name}: #{state}"
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "Poll/loop until job controller is online"
|
83
|
+
task :poll_controller do
|
84
|
+
timeout=ENV['CONTROLLER_TIMEOUT']
|
85
|
+
if timeout.nil? or timeout.empty? then
|
86
|
+
timeout=1200
|
87
|
+
end
|
88
|
+
|
89
|
+
configs=Util.load_configs
|
90
|
+
hash=Util.hash_for_group(configs)
|
91
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
92
|
+
|
93
|
+
puts "Polling for job controller to come online (this may take a couple minutes)..."
|
94
|
+
nodes=nil
|
95
|
+
TorqueVPCToolkit.poll_until_online(hash["vpn-gateway"], timeout, configs) do |nodes_hash|
|
96
|
+
if nodes != nodes_hash then
|
97
|
+
nodes = nodes_hash
|
98
|
+
nodes_hash.each_pair do |name, state|
|
99
|
+
puts "\t#{name}: #{state}"
|
100
|
+
end
|
101
|
+
puts "\t--"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
puts "Job controller online."
|
105
|
+
end
|
106
|
+
|
107
|
+
desc "Poll/loop until jobs finish"
|
108
|
+
task :poll_jobs do
|
109
|
+
timeout=ENV['JOBS_TIMEOUT']
|
110
|
+
if timeout.nil? or timeout.empty? then
|
111
|
+
timeout=3600
|
112
|
+
end
|
113
|
+
|
114
|
+
configs=Util.load_configs
|
115
|
+
hash=Util.hash_for_group(configs)
|
116
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
117
|
+
|
118
|
+
puts "Polling for jobs to finish running..."
|
119
|
+
TorqueVPCToolkit.poll_until_jobs_finished(hash["vpn-gateway"], timeout, configs)
|
120
|
+
puts "Jobs finished."
|
121
|
+
end
|
122
|
+
|
123
|
+
desc "Poll/loop until a range of jobs finishes (requires: FROM_ID=<id>, TO_ID=<id>"
|
124
|
+
task :poll_jobs_range do
|
125
|
+
timeout=ENV['JOBS_TIMEOUT']
|
126
|
+
if timeout.nil? or timeout.empty? then
|
127
|
+
timeout=3600
|
128
|
+
end
|
129
|
+
|
130
|
+
from_id=ENV['FROM_ID']
|
131
|
+
to_id=ENV['TO_ID']
|
132
|
+
|
133
|
+
configs=Util.load_configs
|
134
|
+
hash=Util.hash_for_group(configs)
|
135
|
+
puts "Polling for jobs #{from_id}-#{to_id} to finish running..."
|
136
|
+
TorqueVPCToolkit.poll_until_job_range_finished(hash["vpn-gateway"], Integer(from_id), Integer(to_id),
|
137
|
+
timeout, configs)
|
138
|
+
puts "Jobs finished."
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
desc "Submit a job group and poll until it is complete (requires: JOB_GROUP=<file>)"
|
143
|
+
task :submit_group_and_poll do
|
144
|
+
configs=Util.load_configs
|
145
|
+
hash=Util.hash_for_group(configs)
|
146
|
+
|
147
|
+
initial_max = TorqueVPCToolkit.get_max_job_id(configs, hash)
|
148
|
+
Rake::Task['job:submit_group'].invoke
|
149
|
+
new_max = TorqueVPCToolkit.get_max_job_id(configs, hash)
|
150
|
+
|
151
|
+
ENV['FROM_ID'] = "#{initial_max + 1}"
|
152
|
+
ENV['TO_ID'] = "#{new_max}"
|
153
|
+
Rake::Task['job:poll_jobs_range'].invoke
|
154
|
+
end
|
155
|
+
|
156
|
+
desc "Print job logs for the specified JOB_ID."
|
157
|
+
task :log do
|
158
|
+
job_id=ENV['JOB_ID']
|
159
|
+
|
160
|
+
configs=Util.load_configs
|
161
|
+
hash=Util.hash_for_group(configs)
|
162
|
+
configs.merge!(TorqueVPCToolkit.job_control_credentials(hash['vpn-gateway']))
|
163
|
+
job=TorqueVPCToolkit.job_hash(hash["vpn-gateway"], job_id, configs)
|
164
|
+
|
165
|
+
puts "--"
|
166
|
+
puts "Job ID: #{job['id']}"
|
167
|
+
puts "Description: #{job['description']}"
|
168
|
+
puts "Status: #{job['status']}"
|
169
|
+
puts "--"
|
170
|
+
puts "Stdout:\n#{job['stdout']}"
|
171
|
+
puts "--"
|
172
|
+
puts "Stderr:\n#{job['stderr']}"
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
desc "Poll the controller, sync data, submit and poll all jobs."
|
179
|
+
task :jobs do
|
180
|
+
|
181
|
+
Rake::Task['job:poll_controller'].invoke
|
182
|
+
Rake::Task['share:sync'].invoke
|
183
|
+
Rake::Task['job:submit_all'].invoke
|
184
|
+
Rake::Task['job:poll_jobs'].invoke
|
185
|
+
cleanup=ENV['CLEANUP']
|
186
|
+
|
187
|
+
if cleanup then
|
188
|
+
Rake::Task['group:delete'].invoke
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
desc "DEPRECATED"
|
194
|
+
task :all do
|
195
|
+
|
196
|
+
puts "DEPRECATED: The 'all' task is deprecated. Use the 'rake create && rake jobs' tasks instead."
|
197
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: torque-vpc-toolkit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Dan Prince
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-11 00:00:00 -05:00
|
19
|
+
default_executable: torque-vpc-toolkit
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rake
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: chef-vpc-toolkit
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 2
|
46
|
+
- 0
|
47
|
+
version: "2.0"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: Rake tasks to submit, and poll Torque jobs.
|
51
|
+
email: dan.prince@rackspace.com
|
52
|
+
executables:
|
53
|
+
- torque-vpc-toolkit
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files:
|
57
|
+
- README.rdoc
|
58
|
+
files:
|
59
|
+
- .gitignore
|
60
|
+
- .rvmrc
|
61
|
+
- COPYING
|
62
|
+
- README.rdoc
|
63
|
+
- Rakefile
|
64
|
+
- VERSION
|
65
|
+
- bin/torque-vpc-toolkit
|
66
|
+
- contrib/conf/jobs.json.example
|
67
|
+
- contrib/rake/Rakefile
|
68
|
+
- lib/torque-vpc-toolkit.rb
|
69
|
+
- rake/torque_vpc_toolkit.rake
|
70
|
+
has_rdoc: true
|
71
|
+
homepage: http://github.com/dprince/torque_vpc_toolkit
|
72
|
+
licenses: []
|
73
|
+
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options:
|
76
|
+
- --charset=UTF-8
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
requirements: []
|
98
|
+
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 1.3.7
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Rake tasks to submit Torque jobs.
|
104
|
+
test_files: []
|
105
|
+
|