condor 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|