condor 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +11 -3
- data/bin/condor-exec +19 -14
- data/lib/condor.rb +103 -51
- data/test/test_condor.rb +101 -2
- metadata +2 -2
data/README
CHANGED
@@ -8,19 +8,27 @@ It consists of a wrapper for a Condor submit file and a program, <tt>condor-exec
|
|
8
8
|
|
9
9
|
queues up a job for the program +prog+ with arguments <tt>--option arg1 arg2</tt>. To pass options to <tt>condor-exec</tt> itself, place them before +prog+. For example:
|
10
10
|
|
11
|
-
condor-exec --
|
11
|
+
condor-exec --output=myoutput prog --option arg1 arg2
|
12
12
|
|
13
|
-
Alternately arguments may be specified in a separate arguments file, and one
|
14
|
-
Condor job will be queued for each line in the file
|
13
|
+
Alternately, arguments may be specified in a separate arguments file, and one
|
14
|
+
Condor job will be queued for each line in the file.
|
15
15
|
|
16
16
|
condor-exec --argfile=args prog
|
17
17
|
|
18
18
|
See <tt>condor-exec --help</tt> for more information.
|
19
19
|
|
20
|
+
An <tt>/etc/ruby-condor.yaml</tt> file may be used to specify default attributes for all processes. This is a YAML encoding a of hash. For example, the following will specify the Universe, Getenv, and Log values:
|
21
|
+
|
22
|
+
---
|
23
|
+
Universe: vanilla
|
24
|
+
Getenv: "true"
|
25
|
+
Log: /tmp/$ENV(USER).condor.$(Cluster)
|
26
|
+
|
20
27
|
= History
|
21
28
|
|
22
29
|
1.0.0:: Basic submit file wrapper; <tt>condor-exec</tt> program
|
23
30
|
1.1.0:: Additional options in <tt>condor-exec</tt>, including argument files; Add logging support
|
31
|
+
2.0.0:: Load defaults from configuration file; change command line of condor-exec
|
24
32
|
|
25
33
|
= Copyright
|
26
34
|
|
data/bin/condor-exec
CHANGED
@@ -55,6 +55,9 @@ Switch options appearing before the first non-switch argument are passed to
|
|
55
55
|
'--universe=vanilla' and not '--universe vanilla'). Switch options appearing
|
56
56
|
after the first non-switch argument are treated as arguments to the command.
|
57
57
|
|
58
|
+
The config file option is used to add arbitrary Condor attributes to the header
|
59
|
+
of the submit file. The configuration file should be a YAML encoding of a hash.
|
60
|
+
|
58
61
|
If an argument file is specified, one Condor job is created for each line in
|
59
62
|
the file, using the text of the line as arguments. These arguments are
|
60
63
|
appended after any specified on the command line.
|
@@ -65,6 +68,11 @@ EOS
|
|
65
68
|
options[:argfile] = value
|
66
69
|
end
|
67
70
|
|
71
|
+
opts.on("-cVALUE", "--configfile VALUE",
|
72
|
+
"Configuration filename") do |value|
|
73
|
+
options[:configfile] = value
|
74
|
+
end
|
75
|
+
|
68
76
|
opts.on("-l", "--logging LEVEL", "Logging level") do |level|
|
69
77
|
Condor.set_log_level(eval("Logger::#{level.upcase}"))
|
70
78
|
end
|
@@ -77,23 +85,20 @@ EOS
|
|
77
85
|
options[:error] = value
|
78
86
|
end
|
79
87
|
|
80
|
-
opts.on("-
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
opts.on("-tVALUE", "--transfer-executable VALUE", "Transfer executable") do |value|
|
85
|
-
options[:transfer_executable] = value
|
86
|
-
end
|
87
|
-
|
88
|
-
opts.on("-uVALUE", "--universe VALUE", "Universe") do |value|
|
89
|
-
options[:universe] = value
|
90
|
-
end
|
91
|
-
|
92
|
-
opts.on("-s", "--[no-]submit", "no-submit generates the submit file but does not call condor_submit") do |value|
|
88
|
+
opts.on("-s", "--[no-]submit",
|
89
|
+
"no-submit generates the submit file but does not call condor_submit") do |value|
|
93
90
|
options[:submit] = value
|
94
91
|
end
|
95
92
|
|
96
93
|
end
|
97
94
|
|
98
95
|
command_line = p.parse_command_line
|
99
|
-
|
96
|
+
begin
|
97
|
+
Condor.submit(command_line.shift, command_line, options)
|
98
|
+
rescue Condor::ExecutableException => e
|
99
|
+
puts e
|
100
|
+
if e.instance_of?(Condor::NoExecutableException) and
|
101
|
+
not options == {:submit => true}
|
102
|
+
puts "Did you leave a space in a condor-exec option? (e.g. --output myoutput instead of --output=myoutput)"
|
103
|
+
end
|
104
|
+
end
|
data/lib/condor.rb
CHANGED
@@ -16,13 +16,13 @@
|
|
16
16
|
# editalign; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
17
17
|
# St, Fifth Floor, Boston, MA 02110-1301 USA
|
18
18
|
|
19
|
-
require "erb"
|
20
19
|
require "logger"
|
20
|
+
require "pathname"
|
21
21
|
require "tempfile"
|
22
22
|
|
23
23
|
# Wrapper for the Condor distributed computing system.
|
24
24
|
module Condor
|
25
|
-
VERSION = "
|
25
|
+
VERSION = "2.0.0"
|
26
26
|
|
27
27
|
# Create the logger and set its default log level to ERROR. This function
|
28
28
|
# is called when the module is loaded.
|
@@ -46,71 +46,100 @@ module Condor
|
|
46
46
|
Condor::LOGGER.level = level
|
47
47
|
end
|
48
48
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def initialize(executable,
|
55
|
-
# Get defaults for any unspecified options.
|
56
|
-
@options = {
|
57
|
-
:getenv => "true",
|
58
|
-
:transfer_executable => "false",
|
59
|
-
:universe => "vanilla"}.merge!(options)
|
60
|
-
# Create the submit file text.
|
49
|
+
# Parent class for exceptions raised by this module because of bad
|
50
|
+
# executable files.
|
51
|
+
class ExecutableException < Exception
|
52
|
+
attr_reader :executable
|
53
|
+
|
54
|
+
def initialize(executable, message)
|
61
55
|
@executable = executable
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
56
|
+
super(message)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Exception thrown by SubmitFile when the specified program does not exist.
|
61
|
+
class NoExecutableException < ExecutableException
|
62
|
+
def initialize(executable)
|
63
|
+
super(executable, "Program '#{executable}' does not exist.")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Exception thrown by SubmitFile when the specified program does not have
|
68
|
+
# executable permissions set.
|
69
|
+
class ExecutePermissionException < ExecutableException
|
70
|
+
def initialize(executable)
|
71
|
+
super(executable, "Program '#{executable}' is not executable.")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# The text of a file that can be sent to the <tt>condor_submit</tt> program.
|
76
|
+
class SubmitFile
|
77
|
+
# Specify a single executable and a list of argument sets. Each element
|
78
|
+
# of <tt>argument_list</tt> is a set of arguments. One job will be queued
|
79
|
+
# up for each set of arguments.
|
80
|
+
def initialize(executable, arguments, options = {},
|
81
|
+
output = nil, error = nil)
|
82
|
+
executable.strip!
|
83
|
+
# Read default options from a system-wide configuratation file if one
|
84
|
+
# exists.
|
85
|
+
options = configuration_options.merge(options)
|
66
86
|
# Create the header common to all the jobs.
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
Executable = <%= @executable %>
|
73
|
-
EOTEXT
|
87
|
+
@submit = (options.keys.select do |attribute|
|
88
|
+
not options[attribute].nil?
|
89
|
+
end.collect do |attribute|
|
90
|
+
"#{attribute} = #{options[attribute]}"
|
91
|
+
end + ["executable = #{executable}"]).join("\n") + "\n"
|
74
92
|
# Queue up the arguments for each individual job.
|
75
|
-
|
76
|
-
|
93
|
+
output = current_dir_log_file(executable, "output") if output.nil?
|
94
|
+
error = current_dir_log_file(executable, "error") if error.nil?
|
95
|
+
arguments.each do |job_arguments|
|
96
|
+
@submit += <<-EOTEXT
|
77
97
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
98
|
+
\targuments = #{job_arguments.join(' ')}
|
99
|
+
\toutput = #{output}
|
100
|
+
\terror = #{error}
|
101
|
+
\tQueue
|
82
102
|
EOTEXT
|
83
103
|
end
|
84
|
-
super(template)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Allocate the name of a temporary file in the current directory.
|
88
|
-
def current_dir_log_file(name)
|
89
|
-
"#{File.basename(@executable).strip}.$(Cluster).$(Process).#{name}"
|
90
104
|
end
|
91
105
|
|
92
106
|
# Return a string that can serve as a Condor submit file.
|
93
107
|
def to_s
|
94
|
-
|
108
|
+
@submit
|
95
109
|
end
|
96
|
-
|
97
|
-
|
110
|
+
|
111
|
+
# Read system-wide options from a YAML configuration file. Return an
|
112
|
+
# empty hash if the file does not exist.
|
113
|
+
def configuration_options
|
114
|
+
config = Pathname.new("/etc/ruby-condor.yaml")
|
115
|
+
config.file? ? open(config) {|f| YAML.load(f)} : {}
|
116
|
+
end
|
117
|
+
|
118
|
+
# The name of a temporary file in the current directory.
|
119
|
+
def current_dir_log_file(executable, name)
|
120
|
+
"#{File.basename(executable)}.$(Cluster).$(Process).#{name}"
|
121
|
+
end
|
122
|
+
|
123
|
+
private :current_dir_log_file, :configuration_options
|
98
124
|
end
|
99
125
|
|
100
126
|
|
101
|
-
# This runs the specified executable and arguments as a Condor
|
102
|
-
#
|
103
|
-
# and STDERR are routed to *.output and *.error files with the names of the
|
104
|
-
# executable and the process ID in the name. The Condor log file is written
|
105
|
-
# to the system's temporary directory.
|
127
|
+
# This function runs the specified executable and arguments as a Condor job.
|
128
|
+
# It creates a condor_submit.nnn file in the current directory.
|
106
129
|
def Condor.submit(executable, command_line_arguments, options = {})
|
107
130
|
if executable.nil? or executable.empty?
|
108
131
|
raise "You must specify a command to run"
|
109
132
|
end
|
110
|
-
# Condor needs
|
111
|
-
#
|
112
|
-
executable
|
113
|
-
|
133
|
+
# Condor needs a full path to a executable program. Raise an error if
|
134
|
+
# this cannot be determined.
|
135
|
+
executable.strip!
|
136
|
+
full_exec_paths = File.files_on_path(executable)
|
137
|
+
raise NoExecutableException.new(executable) if full_exec_paths.empty?
|
138
|
+
executables = full_exec_paths.select{|f| File.executable?(f)}
|
139
|
+
if executables.empty?
|
140
|
+
raise ExecutePermissionException.new(full_exec_paths.join(","))
|
141
|
+
end
|
142
|
+
executable = executables.first
|
114
143
|
# Create a unique submit file name of the form condor_submit.nnn.
|
115
144
|
n = 0
|
116
145
|
while n < 1000
|
@@ -132,12 +161,35 @@ Queue
|
|
132
161
|
else
|
133
162
|
[command_line_arguments]
|
134
163
|
end
|
164
|
+
# Optionally read configuration from a file.
|
165
|
+
config = if options.has_key?(:configfile)
|
166
|
+
open(options[:configfile]) {|f| YAML.load(f)}
|
167
|
+
else
|
168
|
+
{}
|
169
|
+
end
|
135
170
|
# Write Condor information to the submit file.
|
136
171
|
File.open(submit_name, "w") do |file|
|
137
|
-
file << Condor::SubmitFile.new(executable, arguments_list,
|
172
|
+
file << Condor::SubmitFile.new(executable, arguments_list, config,
|
173
|
+
options[:output], options[:error])
|
138
174
|
end
|
139
175
|
# Optionally submit the Condor job.
|
140
176
|
`condor_submit #{submit_name}` if options[:submit]
|
141
|
-
end
|
177
|
+
end # Condor.submit
|
142
178
|
|
143
179
|
end # Condor
|
180
|
+
|
181
|
+
# Extensions to the File class.
|
182
|
+
class File
|
183
|
+
# Does the specified full filename have a path component?
|
184
|
+
def File.path_specified?(fullname)
|
185
|
+
not basename(fullname) == fullname
|
186
|
+
end
|
187
|
+
|
188
|
+
# Find all instances of the specified file on the path.
|
189
|
+
def File.files_on_path(name, path = ENV["PATH"])
|
190
|
+
return [name] if path_specified?(name)
|
191
|
+
path.split(File::PATH_SEPARATOR).collect do |p|
|
192
|
+
File.join(p, name)
|
193
|
+
end.select {|f| File.exists?(f)}
|
194
|
+
end
|
195
|
+
end
|
data/test/test_condor.rb
CHANGED
@@ -27,9 +27,108 @@
|
|
27
27
|
require "test/unit"
|
28
28
|
require "condor"
|
29
29
|
|
30
|
+
# A class the simulates the absence of a /etc/ruby-condor.yaml file.
|
31
|
+
class SubmitFileWithoutConfig < Condor::SubmitFile
|
32
|
+
def configuration_options
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# A class the simulates a /etc/ruby-condor.yaml file.
|
38
|
+
class SubmitFileWithConfig < Condor::SubmitFile
|
39
|
+
def configuration_options
|
40
|
+
{"Universe" => "vanilla"}
|
41
|
+
end
|
42
|
+
end
|
30
43
|
|
31
44
|
class CondorTestCase < Test::Unit::TestCase
|
32
|
-
def
|
33
|
-
s =
|
45
|
+
def test_noconfig_submit_file
|
46
|
+
s = SubmitFileWithoutConfig.new("ruby", [["arg1", "arg2"]])
|
47
|
+
expected = <<-EOTEXT
|
48
|
+
executable = ruby
|
49
|
+
|
50
|
+
\targuments = arg1 arg2
|
51
|
+
\toutput = ruby.$(Cluster).$(Process).output
|
52
|
+
\terror = ruby.$(Cluster).$(Process).error
|
53
|
+
\tQueue
|
54
|
+
EOTEXT
|
55
|
+
assert_equal expected, s.to_s
|
34
56
|
end
|
57
|
+
|
58
|
+
def test_config_submit_file
|
59
|
+
s = SubmitFileWithConfig.new("ruby", [["arg1", "arg2"]])
|
60
|
+
expected = <<-EOTEXT
|
61
|
+
Universe = vanilla
|
62
|
+
executable = ruby
|
63
|
+
|
64
|
+
\targuments = arg1 arg2
|
65
|
+
\toutput = ruby.$(Cluster).$(Process).output
|
66
|
+
\terror = ruby.$(Cluster).$(Process).error
|
67
|
+
\tQueue
|
68
|
+
EOTEXT
|
69
|
+
assert_equal expected, s.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_options
|
73
|
+
s = SubmitFileWithoutConfig.new("ruby", [["arg1", "arg2"]],
|
74
|
+
{"Universe" => "vanilla"})
|
75
|
+
expected = <<-EOTEXT
|
76
|
+
Universe = vanilla
|
77
|
+
executable = ruby
|
78
|
+
|
79
|
+
\targuments = arg1 arg2
|
80
|
+
\toutput = ruby.$(Cluster).$(Process).output
|
81
|
+
\terror = ruby.$(Cluster).$(Process).error
|
82
|
+
\tQueue
|
83
|
+
EOTEXT
|
84
|
+
assert_equal expected, s.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_input_output
|
88
|
+
s = SubmitFileWithoutConfig.new("ruby", [["arg1", "arg2"]], {},
|
89
|
+
"output", "error")
|
90
|
+
expected = <<-EOTEXT
|
91
|
+
executable = ruby
|
92
|
+
|
93
|
+
\targuments = arg1 arg2
|
94
|
+
\toutput = output
|
95
|
+
\terror = error
|
96
|
+
\tQueue
|
97
|
+
EOTEXT
|
98
|
+
assert_equal expected, s.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_multiple_arguments
|
102
|
+
s = SubmitFileWithoutConfig.new("ruby", [["arg1", "arg2"],
|
103
|
+
["arg3", "arg4"]])
|
104
|
+
expected = <<-EOTEXT
|
105
|
+
executable = ruby
|
106
|
+
|
107
|
+
\targuments = arg1 arg2
|
108
|
+
\toutput = ruby.$(Cluster).$(Process).output
|
109
|
+
\terror = ruby.$(Cluster).$(Process).error
|
110
|
+
\tQueue
|
111
|
+
|
112
|
+
\targuments = arg3 arg4
|
113
|
+
\toutput = ruby.$(Cluster).$(Process).output
|
114
|
+
\terror = ruby.$(Cluster).$(Process).error
|
115
|
+
\tQueue
|
116
|
+
EOTEXT
|
117
|
+
assert_equal expected, s.to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_overwrite_config
|
121
|
+
s = SubmitFileWithConfig.new("ruby", [["arg1", "arg2"]],
|
122
|
+
{"Universe" => nil})
|
123
|
+
expected = <<-EOTEXT
|
124
|
+
executable = ruby
|
125
|
+
|
126
|
+
\targuments = arg1 arg2
|
127
|
+
\toutput = ruby.$(Cluster).$(Process).output
|
128
|
+
\terror = ruby.$(Cluster).$(Process).error
|
129
|
+
\tQueue
|
130
|
+
EOTEXT
|
131
|
+
assert_equal expected, s.to_s
|
132
|
+
end
|
133
|
+
|
35
134
|
end # CondorTestCase
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: condor
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2009-
|
6
|
+
version: 2.0.0
|
7
|
+
date: 2009-03-03 00:00:00 -08:00
|
8
8
|
summary: Ruby wrapper for the Condor distributed computing system
|
9
9
|
require_paths:
|
10
10
|
- lib
|