jobmanager 1.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/FAQ.txt +151 -0
- data/History.txt +2 -0
- data/LICENSE +20 -0
- data/Manifest.txt +42 -0
- data/README.txt +395 -0
- data/Rakefile +17 -0
- data/bin/jobmanager +20 -0
- data/examples/email_settings.rb +14 -0
- data/examples/example.rb +23 -0
- data/examples/jobmanager.yaml +66 -0
- data/examples/mysql_backup.rb +18 -0
- data/lib/jobmanager.rb +1 -0
- data/lib/jobmanager/application.rb +455 -0
- data/lib/jobmanager/applicationconfig.rb +89 -0
- data/lib/jobmanager/applicationlogger.rb +306 -0
- data/lib/jobmanager/applicationoptionparser.rb +163 -0
- data/lib/jobmanager/system.rb +240 -0
- data/lib/jobmanager/teestream.rb +78 -0
- data/lib/jobmanager/util.rb +25 -0
- data/test/configs/all_values.yaml +33 -0
- data/test/configs/bad_email_condition.yaml +7 -0
- data/test/configs/bad_no_central_log_file.yaml +7 -0
- data/test/configs/bad_number_of_job_logs.yaml +7 -0
- data/test/configs/bad_timeout_zero.yaml +7 -0
- data/test/configs/email_settings.rb +16 -0
- data/test/configs/incomplete_1.yaml +23 -0
- data/test/configs/jobmanager_1.yaml +9 -0
- data/test/configs/jobmanager_2.yaml +11 -0
- data/test/configs/jobmanager_3.yaml +13 -0
- data/test/configs/jobmanager_4_bad_central_log_file.yaml +9 -0
- data/test/configs/jobmanager_4_bad_email_settings_file.yaml +9 -0
- data/test/configs/jobmanager_4_bad_job_logs_directory_1.yaml +9 -0
- data/test/configs/jobmanager_4_bad_job_logs_directory_2.yaml +9 -0
- data/test/configs/minimum_plus_email_settings.yaml +9 -0
- data/test/configs/minimum_required.yaml +5 -0
- data/test/helpers.rb +7 -0
- data/test/mock_syslog.rb +68 -0
- data/test/test_applicationlogger.rb +145 -0
- data/test/test_config.rb +208 -0
- data/test/test_jobmanager.rb +443 -0
- data/test/test_system.rb +206 -0
- data/test/test_teestream.rb +55 -0
- metadata +172 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'configtoolkit'
|
5
|
+
|
6
|
+
module JobManager
|
7
|
+
|
8
|
+
#
|
9
|
+
# This class is a configuration class used to represent the
|
10
|
+
# combination of the configurations specified in the jobmanager
|
11
|
+
# application's configuration file and the the jobmanager
|
12
|
+
# application's command line arguments.
|
13
|
+
#
|
14
|
+
class ApplicationConfig < ConfigToolkit::BaseConfig
|
15
|
+
|
16
|
+
EMAIL_CONDITIONS = [ :always, :never, :on_failure ]
|
17
|
+
CENTRAL_LOG_MODES = [ :syslog, :file ]
|
18
|
+
|
19
|
+
add_required_param(:command, String)
|
20
|
+
|
21
|
+
add_required_param(:job_name, String)
|
22
|
+
|
23
|
+
add_required_param(:job_logs_directory, Pathname)
|
24
|
+
|
25
|
+
add_optional_param(:email_condition, Symbol, :on_failure) do |value|
|
26
|
+
if (EMAIL_CONDITIONS.index(value) == nil)
|
27
|
+
raise_error("email_condition must be one of the following values: #{EMAIL_CONDITIONS.join(', ')}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
add_optional_param(:central_log_mode, Symbol, :syslog) do |value|
|
32
|
+
if (CENTRAL_LOG_MODES.index(value) == nil)
|
33
|
+
modes = CENTRAL_LOG_MODES.join(', ')
|
34
|
+
raise_error("central_log_mode must be one of the following values: #{modes}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
add_optional_param(:number_of_job_logs, Fixnum, 3) do |value|
|
39
|
+
if (value <= 0)
|
40
|
+
raise_error "value must be > 0"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
add_optional_param(:date_time_extension, ConfigToolkit::Boolean, true)
|
45
|
+
|
46
|
+
add_optional_param(:date_time_extension_format, String, '%F_%T') do |value|
|
47
|
+
begin
|
48
|
+
DateTime.now.strftime(value)
|
49
|
+
rescue => e
|
50
|
+
raise_error("Invalid format #{e.message}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
add_optional_param(:zip_rotated_log_file, ConfigToolkit::Boolean, false)
|
55
|
+
|
56
|
+
add_optional_param(:debug, ConfigToolkit::Boolean, false)
|
57
|
+
|
58
|
+
add_optional_param(:central_log_file, Pathname)
|
59
|
+
|
60
|
+
add_optional_param(:command_path, String, ENV['PATH'])
|
61
|
+
|
62
|
+
add_optional_param(:timeout, Fixnum) do |value|
|
63
|
+
if (value <= 0)
|
64
|
+
raise_error("timeout must be a positive integer (seconds)")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
add_optional_param(:email_settings_file, Pathname, "email_settings.rb")
|
69
|
+
|
70
|
+
add_optional_param(:email_subject, String,
|
71
|
+
"jobmanager results for <%=job_name%> on <%=host_name%> : <%=result%>")
|
72
|
+
|
73
|
+
add_optional_param(:email_from_address, String)
|
74
|
+
|
75
|
+
add_optional_param(:email_to_address, String)
|
76
|
+
|
77
|
+
def validate_all_values()
|
78
|
+
if (self.central_log_mode == :file && !self.central_log_file?)
|
79
|
+
raise_error("If central_log_mode is set to file, central_log_file must be specified.")
|
80
|
+
end
|
81
|
+
|
82
|
+
if (self.central_log_mode == :syslog && self.central_log_file?)
|
83
|
+
raise_error("If central_log_mode is set to syslog, central_log_file should not be specified.")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'syslog'
|
2
|
+
require 'logger'
|
3
|
+
require 'etc'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'syslog_logger'
|
7
|
+
|
8
|
+
|
9
|
+
module JobManager
|
10
|
+
|
11
|
+
#
|
12
|
+
# This class is a base class for all of the ApplicationLogger*
|
13
|
+
# classes implemented in this file. This class should not be
|
14
|
+
# instantiated directly. This class derives from IO so that it can
|
15
|
+
# reuse the print methods (print, <<, puts, etc).
|
16
|
+
#
|
17
|
+
class ApplicationLogger < IO
|
18
|
+
|
19
|
+
#
|
20
|
+
# This constant is a map between the shortcut methods in the
|
21
|
+
# Logger interface (debug, info, warn, etc) and their associated
|
22
|
+
# severity constants.
|
23
|
+
#
|
24
|
+
LEVEL_LOGGER_MAP = SyslogLogger::LOGGER_LEVEL_MAP.invert
|
25
|
+
|
26
|
+
attr_accessor :job_name
|
27
|
+
attr_accessor :user_name
|
28
|
+
|
29
|
+
def initialize(job_name, user_name)
|
30
|
+
@job_name = job_name ? job_name : "UNKNOWN"
|
31
|
+
@user_name = user_name
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# ====Description:
|
36
|
+
# This method logs the given exception.
|
37
|
+
#
|
38
|
+
def record_exception(e)
|
39
|
+
error(e.message)
|
40
|
+
debug(e.backtrace.join("\n"))
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# ====Description:
|
45
|
+
# This method logs the given exception and the associated tag.
|
46
|
+
#
|
47
|
+
def record_tagged_exception(message, e)
|
48
|
+
error("#{message}: Error (#{e.class}): #{e.message}")
|
49
|
+
debug(e.backtrace.join("\n"))
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# ====Description:
|
54
|
+
# This method logs a message with severity level Logger::INFO.
|
55
|
+
#
|
56
|
+
def write(message)
|
57
|
+
info(message)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# This class provides a Logger interface and logs messages to syslog
|
64
|
+
# via the SysLogLogger gem. In addition, this class provides a
|
65
|
+
# string instance method which allows the caller to retrieve a
|
66
|
+
# concatenation of all the messages written to this logger. The
|
67
|
+
# level, user name, and job name are prepended to each message that
|
68
|
+
# is written to syslog. A number of the interface methods are
|
69
|
+
# implemented via the method_missing method.
|
70
|
+
#
|
71
|
+
class ApplicationSyslogLogger < ApplicationLogger
|
72
|
+
#
|
73
|
+
# ====Description:
|
74
|
+
# This method creates a new instance.
|
75
|
+
#
|
76
|
+
def initialize(program_name,
|
77
|
+
user_name,
|
78
|
+
job_name = nil)
|
79
|
+
|
80
|
+
super(job_name, user_name)
|
81
|
+
|
82
|
+
@program_name = program_name
|
83
|
+
@stringio = StringIO.new
|
84
|
+
|
85
|
+
@logger = SyslogLogger.new(program_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# ====Returns:
|
90
|
+
# A string containing all mesages that were logged through this
|
91
|
+
# interface.
|
92
|
+
#
|
93
|
+
def string()
|
94
|
+
return @stringio.string()
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# ====Description:
|
99
|
+
# This method is akin to the Logger::add interface method.
|
100
|
+
#
|
101
|
+
def add(severity, message = nil, &block)
|
102
|
+
wrap_record_send(LEVEL_LOGGER_MAP[severity], message || yield)
|
103
|
+
end
|
104
|
+
alias log add
|
105
|
+
|
106
|
+
#
|
107
|
+
# ====Description:
|
108
|
+
# This method closes the connection to the syslog daemon.
|
109
|
+
#
|
110
|
+
def close
|
111
|
+
if (Syslog.opened?) then Syslog.close() end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
#
|
117
|
+
# ====Description:
|
118
|
+
# This method is intended to handle all shortcut methods (debug,
|
119
|
+
# warn, info, etc.), as well as the level attribute methods.
|
120
|
+
#
|
121
|
+
def method_missing(*args, &block)
|
122
|
+
method = args.shift
|
123
|
+
|
124
|
+
# if the method is a shortcut log method
|
125
|
+
if (SyslogLogger::LOGGER_MAP.find() {|key, value| method == key})
|
126
|
+
# wrap the message (with the job and user names)
|
127
|
+
wrap_record_send(method, (args.length > 0) ? args[0] : yield)
|
128
|
+
else
|
129
|
+
# otherwise forward the message directly on to the SyslogLogger instance.
|
130
|
+
@logger.send(method, *args, &block)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def wrap(method, message)
|
135
|
+
level = method.to_s.upcase
|
136
|
+
return "#{level} [#{user_name()}, #{job_name()}] #{message}"
|
137
|
+
end
|
138
|
+
|
139
|
+
def wrap_record_send(method, message)
|
140
|
+
if (!message) then return end
|
141
|
+
|
142
|
+
# if the level of the message is within the level of the logger
|
143
|
+
if (SyslogLogger::LOGGER_LEVEL_MAP[method] >= @logger.level)
|
144
|
+
lines = message.split("\n")
|
145
|
+
|
146
|
+
lines.each do |line|
|
147
|
+
wrapped_line = wrap(method, line)
|
148
|
+
@logger.send(method, wrapped_line)
|
149
|
+
@stringio << wrapped_line << "\n"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
#
|
156
|
+
# This class provides a Logger interface and logs messages to an IO
|
157
|
+
# stream. In addition, it provides a string instance method which
|
158
|
+
# allows the caller to retrieve a concatenation of all the messages
|
159
|
+
# written to this logger (with additional formatting such that the
|
160
|
+
# string represents exactly what was written to the IO stream).
|
161
|
+
# This class is implemented using the Logger class itself. The user
|
162
|
+
# name, and job name are prepended to each message, which is then
|
163
|
+
# written via the Logger class (to a string stream) which is in turn
|
164
|
+
# written to the IO stream. This was done so that the exact string
|
165
|
+
# that was logged to the IO stream could be later returned via the
|
166
|
+
# string instance method. A number of the interface methods are
|
167
|
+
# implemented via the method_missing method.
|
168
|
+
#
|
169
|
+
class ApplicationIOLogger < ApplicationLogger
|
170
|
+
|
171
|
+
LOGGER_LEVELS = [ :debug,
|
172
|
+
:info,
|
173
|
+
:warn,
|
174
|
+
:error,
|
175
|
+
:fatal,
|
176
|
+
:unknown
|
177
|
+
]
|
178
|
+
|
179
|
+
#
|
180
|
+
# ====Description:
|
181
|
+
# This method creates a new instance.
|
182
|
+
#
|
183
|
+
def initialize(io_stream,
|
184
|
+
user_name,
|
185
|
+
job_name = nil)
|
186
|
+
|
187
|
+
super(job_name, user_name)
|
188
|
+
|
189
|
+
@copy = StringIO.new
|
190
|
+
@io_stream = io_stream
|
191
|
+
|
192
|
+
@logger_stringio = StringIO.new
|
193
|
+
@logger = Logger.new(@logger_stringio)
|
194
|
+
@logger.formatter = Logger::Formatter.new
|
195
|
+
@logger.datetime_format = '%FT%T '
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# ====Returns:
|
200
|
+
# A string containing all mesages that were logged through this
|
201
|
+
# interface.
|
202
|
+
#
|
203
|
+
def string()
|
204
|
+
return @copy.string()
|
205
|
+
end
|
206
|
+
|
207
|
+
#
|
208
|
+
# ====Description:
|
209
|
+
# This method is akin to the Logger::add interface method.
|
210
|
+
#
|
211
|
+
def add(severity, message = nil, &block)
|
212
|
+
wrap_record_send(LEVEL_LOGGER_MAP[severity], message || yield)
|
213
|
+
end
|
214
|
+
alias log add
|
215
|
+
|
216
|
+
#
|
217
|
+
# ====Description:
|
218
|
+
# This method exists only to complete the Logger interface. It
|
219
|
+
# is a noop.
|
220
|
+
#
|
221
|
+
def close
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
def wrap(message)
|
226
|
+
if (!message) then return nil end
|
227
|
+
|
228
|
+
return "[#{user_name()}, #{job_name()}] #{message}"
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# ====Description:
|
233
|
+
# This method captures the output of the Logger instance and
|
234
|
+
# forwards it to the contained iostream and the aggregated string
|
235
|
+
# available for retrieval via the string method.
|
236
|
+
#
|
237
|
+
def wrap_record_send(method, message)
|
238
|
+
lines = message.split("\n")
|
239
|
+
lines.each do |line|
|
240
|
+
@logger.send(method, wrap(line))
|
241
|
+
end
|
242
|
+
|
243
|
+
output = @logger_stringio.string()
|
244
|
+
@logger_stringio.string = ""
|
245
|
+
|
246
|
+
@io_stream << output
|
247
|
+
@copy << output
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# ====Description:
|
252
|
+
# This method is intended to handle all shortcut methods (debug,
|
253
|
+
# warn, info, etc.), as well as the level attribute methods.
|
254
|
+
#
|
255
|
+
def method_missing(*args, &block)
|
256
|
+
method = args.shift
|
257
|
+
|
258
|
+
# if the method is a shortcut log method
|
259
|
+
if (LOGGER_LEVELS.find() {|key| method == key})
|
260
|
+
wrap_record_send(method, args[0] ? args[0] : yield)
|
261
|
+
else
|
262
|
+
# forward the method directly onto the Logger instance.
|
263
|
+
@logger.send(method, *args, &block)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
#
|
269
|
+
# This class provides a Logger interface and logs messages to a
|
270
|
+
# file. In addition, it provides a string instance method which
|
271
|
+
# allows the caller to retrieve a concatenation of all the messages
|
272
|
+
# written to this logger. A number of the interface methods are
|
273
|
+
# implemented via the method_missing method.
|
274
|
+
#
|
275
|
+
class ApplicationFileLogger < ApplicationIOLogger
|
276
|
+
|
277
|
+
#
|
278
|
+
# ====Description:
|
279
|
+
# This method creates a new instance. All further messages
|
280
|
+
# written to this instance will be in turn written to the
|
281
|
+
# specified log file.
|
282
|
+
#
|
283
|
+
def initialize(log_file,
|
284
|
+
user_name,
|
285
|
+
job_name = nil)
|
286
|
+
|
287
|
+
FileUtils.mkdir_p(File.dirname(log_file))
|
288
|
+
@log_file_stream = File.open(log_file, "a")
|
289
|
+
|
290
|
+
super(@log_file_stream,
|
291
|
+
user_name,
|
292
|
+
job_name)
|
293
|
+
end
|
294
|
+
|
295
|
+
#
|
296
|
+
# ====Description:
|
297
|
+
# This method closes the log file stream.
|
298
|
+
#
|
299
|
+
def close
|
300
|
+
@log_file_stream.close()
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
|
@@ -0,0 +1,163 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'jobmanager/applicationconfig.rb'
|
5
|
+
|
6
|
+
module JobManager
|
7
|
+
|
8
|
+
#
|
9
|
+
# This class is composed of the command line argument parsing code
|
10
|
+
# for the jobmanager application.
|
11
|
+
#
|
12
|
+
class ApplicationOptionParser
|
13
|
+
|
14
|
+
#
|
15
|
+
# ====Description:
|
16
|
+
# This method parses the command line options and arguments passed
|
17
|
+
# in and returns a hash of these values.
|
18
|
+
# ====Parameters:
|
19
|
+
# [program_name]
|
20
|
+
# The program name.
|
21
|
+
# [args]
|
22
|
+
# The command line arguments.
|
23
|
+
# ====Returns:
|
24
|
+
# A hash of the command line options and arugments.
|
25
|
+
#
|
26
|
+
def self.parse(program_name, args)
|
27
|
+
options = {}
|
28
|
+
opts = OptionParser.new
|
29
|
+
|
30
|
+
opts.program_name = program_name
|
31
|
+
opts.summary_width = 40
|
32
|
+
opts.banner = get_usage(program_name)
|
33
|
+
|
34
|
+
opts.separator ""
|
35
|
+
opts.separator "Specific options:"
|
36
|
+
|
37
|
+
is_job_name_set = false
|
38
|
+
opts.on("-j", "--job_name NAME",
|
39
|
+
"The name of the job to be run, and thus the basename of the log file.") do |job_name|
|
40
|
+
options["job_name"] = job_name
|
41
|
+
is_job_name_set = true
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("-l", "--job_logs_directory PATH",
|
45
|
+
"The directory in which the job logs will be kept.") do |logs_directory|
|
46
|
+
options["job_logs_directory"] = logs_directory
|
47
|
+
end
|
48
|
+
|
49
|
+
conditions = JobManager::ApplicationConfig::EMAIL_CONDITIONS.join(', ')
|
50
|
+
opts.on("-c", "--email_condition CONDITION",
|
51
|
+
"The condition upon which results should be emailed.",
|
52
|
+
"Possible values are:(#{conditions}.") do |condition|
|
53
|
+
options["email_condition"] = condition
|
54
|
+
end
|
55
|
+
|
56
|
+
modes = JobManager::ApplicationConfig::CENTRAL_LOG_MODES.join(', ')
|
57
|
+
opts.on("-m", "--central_log_mode MODE",
|
58
|
+
"Possible values are #{modes}.") do |mode|
|
59
|
+
options["central_log_mode"] = mode
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on("-n", "--number_of_job_logs LOGS",
|
63
|
+
"Number of log files to be kept per job.") do |number_of_logs|
|
64
|
+
options["number_of_job_logs"] = number_of_logs
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on("-x", "--[no-]date_time_extension",
|
68
|
+
"Whether to add a date/time extension to the rotated log file.") do |value|
|
69
|
+
options["date_time_extension"] = value
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on("-f", "--date_time_extension_format FORMAT",
|
73
|
+
"The date/time format of the rotated log file extension.") do |format|
|
74
|
+
options["date_time_extension_format"] = format
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.on("-z", "--[no-]zip_rotated_log_file",
|
78
|
+
"Zip the rotated log file.") do |value|
|
79
|
+
options["zip_rotated_log_file"] = value
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.on("-r", "--central_log_file FILE",
|
83
|
+
"The central log file to which jobmanager writes.",
|
84
|
+
"This field is only valid if central_log_mode is set to \"file\".") do |file|
|
85
|
+
options["central_log_file"] = file
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("-t", "--timeout TIMEOUT", OptionParser::DecimalInteger,
|
89
|
+
"Timeout (in seconds) after which the script is killed.") do |timeout|
|
90
|
+
options["timeout"] = timeout
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.on("-e", "--email_settings_file PATH",
|
94
|
+
String,
|
95
|
+
"The configuration file for the simpleemail gem.",
|
96
|
+
"Note: If a relative path is specified, it will be considered to be relative to the",
|
97
|
+
"location of the configuration file.") do |file|
|
98
|
+
options["email_settings_file"] = file
|
99
|
+
end
|
100
|
+
|
101
|
+
opts.on("-o", "--email_from_address ADDRESS",
|
102
|
+
"The email address from which jobmanager sends results.") do |address|
|
103
|
+
options["email_from_address"] = address
|
104
|
+
end
|
105
|
+
|
106
|
+
opts.on("-o", "--email_to_address ADDRESS",
|
107
|
+
"The email address to which jobmanager sends results.") do |address|
|
108
|
+
options["email_to_address"] = address
|
109
|
+
end
|
110
|
+
|
111
|
+
opts.on("-p", "--command_path PATH",
|
112
|
+
"The path to search for the command that jobmanager is invoked with.") do |path|
|
113
|
+
options["command_path"] = path
|
114
|
+
end
|
115
|
+
|
116
|
+
opts.on("-s", "--email_subject SUBJECT",
|
117
|
+
"The email subject, to be interpreted by ERB.",
|
118
|
+
"Allowed variables: (job_name, command, host_name, result, user_name)") do |subject|
|
119
|
+
options["email_subject"] = subject
|
120
|
+
end
|
121
|
+
|
122
|
+
opts.on_tail("-d", "--[no-]debug", "Turn on/off debug trace.") do |value|
|
123
|
+
options["debug"] = value
|
124
|
+
end
|
125
|
+
|
126
|
+
opts.on_tail("-h", "--help", "Show this message.") do
|
127
|
+
puts opts, "\n"
|
128
|
+
exit(0)
|
129
|
+
end
|
130
|
+
|
131
|
+
opts.parse!(args)
|
132
|
+
|
133
|
+
if (args.length != 1)
|
134
|
+
puts opts, "\n"
|
135
|
+
raise ArgumentError, "One argument (command) is required!"
|
136
|
+
end
|
137
|
+
|
138
|
+
options["command"] = args.shift()
|
139
|
+
|
140
|
+
if (!is_job_name_set)
|
141
|
+
exe = options["command"].split(' ')[0]
|
142
|
+
options["job_name"] = File.basename(exe)
|
143
|
+
end
|
144
|
+
|
145
|
+
options
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def self.get_usage(program_name)
|
150
|
+
usage = <<-EOL
|
151
|
+
|
152
|
+
Usage: #{program_name} [options] <command>
|
153
|
+
|
154
|
+
See http://jobmanager.rubyforge.com for a more detailed description and usage examples.
|
155
|
+
|
156
|
+
EOL
|
157
|
+
|
158
|
+
usage
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|