condor 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +11 -3
  2. data/bin/condor-exec +19 -14
  3. data/lib/condor.rb +103 -51
  4. data/test/test_condor.rb +101 -2
  5. 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 --universe=MPI prog --option arg1 arg2
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
 
@@ -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("-gVALUE", "--getenv VALUE", "Get environment") do |value|
81
- options[:getenv] = value
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
- Condor.submit(command_line.shift, command_line, options)
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
@@ -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 = "1.1.0"
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
- # The text of a file that can be sent to the condor_submit program.
50
- class SubmitFile < ERB
51
- # Specify a single executable and a list of argument sets. Each element
52
- # of argument_list is a set of arguments. One job will be queued up for
53
- # each set of arguments.
54
- def initialize(executable, argument_list, options = {})
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
- @argument_list = argument_list
63
- @log = File.join(Dir::tmpdir, "#{ENV['USER']}.condor.$(Cluster).$(Process)")
64
- @output = options.fetch(:output, current_dir_log_file("output"))
65
- @error = options.fetch(:error, current_dir_log_file("error"))
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
- template = <<-EOTEXT
68
- Universe = <%= @options[:universe] %>
69
- getenv = <%= @options[:getenv] %>
70
- transfer_executable = <%= @options[:transfer_executable] %>
71
- Log = <%= @log %>
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
- argument_list.each_with_index do |arguments, i|
76
- template += <<-EOTEXT
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
- arguments = <%= @argument_list[#{i}].join(" ") %>
79
- output = <%= @output %>
80
- error = <%= @error %>
81
- Queue
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
- result(binding)
108
+ @submit
95
109
  end
96
-
97
- private :current_dir_log_file
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 command. The
102
- # function creates a condor_submit.nnn file in the current directory. STDOUT
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 the full path to the command.
111
- # BUGBUG This is UNIX specific.
112
- executable = `which #{executable} 2> /dev/null`.strip
113
- not executable.empty? or raise "#{executable} is not executable."
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, options)
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
@@ -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 test_submit_file
33
- s = Condor::SubmitFile.new("ruby", [["arg1", "arg2"]])
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: 1.1.0
7
- date: 2009-02-12 00:00:00 -08:00
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