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
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
require 'rubygems'
|
3
|
+
require 'hoe'
|
4
|
+
|
5
|
+
Hoe.new('jobmanager', "1.0.0") do |p|
|
6
|
+
p.remote_rdoc_dir = ''
|
7
|
+
p.developer('DesigningPatterns', 'technical.inquiries@designingpatterns.com')
|
8
|
+
|
9
|
+
p.extra_deps << ['simpleemail']
|
10
|
+
p.extra_deps << ['configtoolkit', '>= 2.2']
|
11
|
+
p.extra_deps << ['relative']
|
12
|
+
p.extra_deps << ['logrotate']
|
13
|
+
p.extra_deps << ['SyslogLogger']
|
14
|
+
p.extra_deps << ['sys-host']
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: syntax=Ruby
|
data/bin/jobmanager
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'relative'
|
5
|
+
require 'jobmanager'
|
6
|
+
|
7
|
+
PROGRAM_NAME = File.basename($0)
|
8
|
+
|
9
|
+
begin
|
10
|
+
env_config_file = ENV["JOBMANAGER_CONFIG_FILE"]
|
11
|
+
config_file = env_config_file ? env_config_file : "/etc/jobmanager.yaml"
|
12
|
+
|
13
|
+
jobmanager = JobManager::Application.new(PROGRAM_NAME, ARGV, config_file)
|
14
|
+
jobmanager.run()
|
15
|
+
rescue => error
|
16
|
+
# All relevant errors have already been printed.
|
17
|
+
exit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
ActionMailer::Base.delivery_method = :smtp
|
2
|
+
|
3
|
+
ActionMailer::Base.smtp_settings = {
|
4
|
+
:address => 'smtp.gmail.com',
|
5
|
+
:port => 587,
|
6
|
+
:domain => "gmail.com",
|
7
|
+
:authentication => "login",
|
8
|
+
:user_name => "sender_address@gmail.com",
|
9
|
+
:password => "password",
|
10
|
+
:tls => :auto
|
11
|
+
}
|
12
|
+
|
13
|
+
SimpleEmail::Email.default_from = "Sender Address <sender_address@gmail.com>"
|
14
|
+
SimpleEmail::Email.default_to = "Receiver Address <receiver_address@gmail.com>"
|
data/examples/example.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# These example programs use the 'relative' gem in order to express
|
5
|
+
# paths relative to __FILE__ cleanly, allowing them to be run from any
|
6
|
+
# directory. In particular, they use
|
7
|
+
# File.expand_path_relative_to_caller in order to refer to
|
8
|
+
# configuration files within the examples directory.
|
9
|
+
#
|
10
|
+
require 'rubygems'
|
11
|
+
require 'relative'
|
12
|
+
|
13
|
+
CONFIGURATION_FILE = File.expand_path_relative_to_caller("jobmanager.yaml")
|
14
|
+
|
15
|
+
ENV["JOBMANAGER_CONFIG_FILE"] = CONFIGURATION_FILE
|
16
|
+
|
17
|
+
command = "jobmanager --job_name ls_usr \"ls /usr\""
|
18
|
+
print "Command: #{command}\n"
|
19
|
+
|
20
|
+
output = %x[ #{command} ]
|
21
|
+
|
22
|
+
print "Output:\n"
|
23
|
+
print output
|
@@ -0,0 +1,66 @@
|
|
1
|
+
################################################################################
|
2
|
+
# The jobmanager configuration file.
|
3
|
+
#
|
4
|
+
# For a full description of each of the fields, and their default values,
|
5
|
+
# see the CONFIGURATION FILE section in README.txt.
|
6
|
+
################################################################################
|
7
|
+
|
8
|
+
# The number of log files to be kept per job.
|
9
|
+
number_of_job_logs: 5
|
10
|
+
|
11
|
+
# The directory in which the job logs will be kept, to be interpreted
|
12
|
+
# by ERB. Allowed variables: user_name.
|
13
|
+
job_logs_directory: /tmp/logs
|
14
|
+
|
15
|
+
# jobmanager logs operational information (when jobs are launched,
|
16
|
+
# exit, etc). This can be done via syslog or directly to a log file.
|
17
|
+
# Possible values are: syslog, file.
|
18
|
+
central_log_mode: file
|
19
|
+
|
20
|
+
# If the central_log_mode is set to "file", the log file must be
|
21
|
+
# specified. This parameter will be interpreted by ERB. Allowed
|
22
|
+
# variables: user_name.
|
23
|
+
central_log_file: /tmp/logs/jobmanager.log
|
24
|
+
|
25
|
+
# The path to search for the command that jobmanager is invoked with.
|
26
|
+
#command_path: /usr/bin:/usr/designingpatterns/bin
|
27
|
+
|
28
|
+
# Whether jobmanager should print debug trace to the central log file.
|
29
|
+
debug: false
|
30
|
+
|
31
|
+
# The condition upon which condition results should be emailed.
|
32
|
+
# Possible values are: always, never, on_failure.
|
33
|
+
email_condition: always
|
34
|
+
|
35
|
+
# The configuration file for the simpleemail gem. This parameter will
|
36
|
+
# be interpreted by ERB. Allowed variables: user_name. Note: If a
|
37
|
+
# relative path is specified, it will be considered to be relative to
|
38
|
+
# the location of this configuration file.
|
39
|
+
email_settings_file: email_settings.rb
|
40
|
+
|
41
|
+
# The email subject, to be interpreted by ERB.
|
42
|
+
# Allowed variables: job_name, command, host_name, results, user_name.
|
43
|
+
email_subject: "jobmanager results for <%=job_name%> on <%=host_name%> : <%=result%>"
|
44
|
+
|
45
|
+
# The extension of the rotated job log file. If true, the extension
|
46
|
+
# is a date_time. Otherwise, it is a simple count (.1, .2, etc.).
|
47
|
+
date_time_extension: true
|
48
|
+
|
49
|
+
# If date_time_extension is true, this field will be used as the
|
50
|
+
# format for the date/time extension of the rotated job log file
|
51
|
+
# name.
|
52
|
+
date_time_extension_format: '%F_%T'
|
53
|
+
|
54
|
+
# Whether the rotated job log file should be zipped.
|
55
|
+
zip_rotated_log_file: false
|
56
|
+
|
57
|
+
# The timeout (in seconds). If not present, no timeout will be used.
|
58
|
+
timeout: 1800
|
59
|
+
|
60
|
+
# The sender of emails sent from jobmanager. This can also be
|
61
|
+
# specified in the email_settings_file (attribute default_from).
|
62
|
+
#email_from_address: "Sender <sender@gmail.com>"
|
63
|
+
|
64
|
+
# The receiver of emails sent from jobmanager. This can also be
|
65
|
+
# specified in the email_settings_file (attributes default_to).
|
66
|
+
#email_to_address: "Receiver <receiver@gmail.com>"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
print "=================================================\n"
|
4
|
+
print "Backing up Database\n"
|
5
|
+
print "=================================================\n"
|
6
|
+
|
7
|
+
database = ARGV[0]
|
8
|
+
|
9
|
+
command = "mysqldump #{database} > /tmp/#{database}.mysql"
|
10
|
+
print command, "\n"
|
11
|
+
|
12
|
+
if (system("#{command}"))
|
13
|
+
print"\nSuccess!\n"
|
14
|
+
else
|
15
|
+
print "\nFailure!\n"
|
16
|
+
exit(1)
|
17
|
+
end
|
18
|
+
|
data/lib/jobmanager.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'jobmanager/application'
|
@@ -0,0 +1,455 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'logrotate'
|
9
|
+
require 'relative'
|
10
|
+
require 'sys/host'
|
11
|
+
require 'configtoolkit'
|
12
|
+
require 'configtoolkit/yamlreader'
|
13
|
+
require 'configtoolkit/hashreader'
|
14
|
+
require 'configtoolkit/overridereader'
|
15
|
+
|
16
|
+
require 'jobmanager/util'
|
17
|
+
|
18
|
+
# The simpleemail gem will bring in a bunch of ActionMailer warnings.
|
19
|
+
# Disable this temporarily.
|
20
|
+
JobManager.disable_warnings { require 'simpleemail' }
|
21
|
+
|
22
|
+
require 'jobmanager/applicationconfig'
|
23
|
+
require 'jobmanager/applicationoptionparser'
|
24
|
+
require 'jobmanager/system'
|
25
|
+
require 'jobmanager/teestream'
|
26
|
+
require 'jobmanager/applicationlogger'
|
27
|
+
|
28
|
+
module JobManager
|
29
|
+
|
30
|
+
class Application
|
31
|
+
|
32
|
+
# Inside the Application class, two classes of exceptions are
|
33
|
+
# raised (Error and ComposedError). This is done so that when an
|
34
|
+
# exception is caught, the exception handling code can discern
|
35
|
+
# between an exception that this Application has raised and an
|
36
|
+
# unexpected exception (raised in code other than this class).
|
37
|
+
# The exception handling code can than log this information.
|
38
|
+
|
39
|
+
# A simple exception class used in this class.
|
40
|
+
class Error < StandardError
|
41
|
+
end
|
42
|
+
|
43
|
+
# A simple exception class that repackages an existing exception.
|
44
|
+
# This class often catches exceptions raised by other libraries,
|
45
|
+
# and repackages them, adding additional trace (via the msg
|
46
|
+
# parameter).
|
47
|
+
class ComposedError < Error
|
48
|
+
def initialize(msg, contained_exception, backtrace = caller())
|
49
|
+
@msg = msg
|
50
|
+
@contained_exception = contained_exception
|
51
|
+
|
52
|
+
super(msg)
|
53
|
+
set_backtrace(backtrace)
|
54
|
+
end
|
55
|
+
|
56
|
+
def contained_exception()
|
57
|
+
return @contained_exception
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# ====Description:
|
63
|
+
# This method creates an Application instance. It also reads in
|
64
|
+
# the configuration file and the command line options.
|
65
|
+
#
|
66
|
+
# ====Parameters:
|
67
|
+
# [program_name]
|
68
|
+
# The name of the program.
|
69
|
+
# [argv]
|
70
|
+
# A list of command line arguments.
|
71
|
+
# [config_file]
|
72
|
+
# The jobmanager configuration file name.
|
73
|
+
# [override_terminal_shell]
|
74
|
+
# Whether to override this method's internal determination of
|
75
|
+
# whether the calling process has been invoked by a shell
|
76
|
+
# attached to a terminal. If the value is nil, this method
|
77
|
+
# will make its own determination. This is purely for
|
78
|
+
# debugging purposes.
|
79
|
+
#
|
80
|
+
def initialize(program_name,
|
81
|
+
argv,
|
82
|
+
config_file,
|
83
|
+
override_terminal_shell = nil)
|
84
|
+
|
85
|
+
begin
|
86
|
+
@mgr_logger = nil
|
87
|
+
@email_ready = false
|
88
|
+
@program_name = program_name
|
89
|
+
@argv = argv
|
90
|
+
@config_file = config_file
|
91
|
+
@user_name = Etc.getpwuid(Process::Sys.getuid).name()
|
92
|
+
|
93
|
+
@terminal_shell = STDIN.tty? || STDOUT.tty? || STDERR.tty?
|
94
|
+
if (override_terminal_shell != nil)
|
95
|
+
@terminal_shell = override_terminal_shell
|
96
|
+
end
|
97
|
+
|
98
|
+
# If any of this process's standard iostreams are connected to
|
99
|
+
# a terminal, this process has been invoked directly or
|
100
|
+
# indirectly by the command line shell. As a result, let's log
|
101
|
+
# central log messages to STDOUT.
|
102
|
+
if (@terminal_shell)
|
103
|
+
@mgr_logger = ApplicationIOLogger.new(STDOUT,
|
104
|
+
@user_name)
|
105
|
+
|
106
|
+
str = "\n"
|
107
|
+
str << "*NOTE* "
|
108
|
+
str << "jobmanager has been invoked interactively. Ignoring the "
|
109
|
+
str << "central_log_* configuration parameters, and logging all output to "
|
110
|
+
str << "the terminal.\n\n"
|
111
|
+
|
112
|
+
print str
|
113
|
+
|
114
|
+
else
|
115
|
+
# None of this process's standard iostreams are connected to
|
116
|
+
# a terminal. This process has most likely been invoked by
|
117
|
+
# cron. Log all central log messages to syslog. The user
|
118
|
+
# may have specified in the configuration file for all
|
119
|
+
# central log messages to be directed to a file. However,
|
120
|
+
# until we successfully process the configuration file, we
|
121
|
+
# do not know the log mode they have chosen. Thus,
|
122
|
+
# temporarily log central log messages to syslog until we
|
123
|
+
# have read this configuration parameter. No messages will
|
124
|
+
# actually be written to syslog unless an error condition
|
125
|
+
# occurs (such as not being able to read or parse the
|
126
|
+
# configuration file itself!)
|
127
|
+
@mgr_logger = ApplicationSyslogLogger.new(@program_name,
|
128
|
+
@user_name)
|
129
|
+
end
|
130
|
+
|
131
|
+
# @mgr_logger is now valid! We can print to @mgr_logger in
|
132
|
+
# rescue statements going forwards.
|
133
|
+
# This method populates @config with the configuration from
|
134
|
+
# the configuration file and the command line parameters.
|
135
|
+
gather_options(@program_name, @argv, @config_file)
|
136
|
+
|
137
|
+
# This is done so that if any methods called by this
|
138
|
+
# application call any of the standard print commands (print,
|
139
|
+
# puts, etc) directly, these messages will be printed to
|
140
|
+
# @mgr_logger.
|
141
|
+
$stderr = $stdout = @mgr_logger
|
142
|
+
|
143
|
+
# Print the config in debug mode.
|
144
|
+
if (@mgr_logger.level <= Logger::DEBUG)
|
145
|
+
@mgr_logger.debug("Configuration:\n #{@config}\n")
|
146
|
+
end
|
147
|
+
rescue => e
|
148
|
+
handle_exception(e)
|
149
|
+
# Re-raise the exception to notify bin/jobmanager to exit!
|
150
|
+
raise e
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def handle_exception(e)
|
155
|
+
if (@mgr_logger)
|
156
|
+
case e
|
157
|
+
when ComposedError
|
158
|
+
@mgr_logger.record_tagged_exception(e.message, e.contained_exception)
|
159
|
+
when Error
|
160
|
+
@mgr_logger.record_exception(e)
|
161
|
+
else
|
162
|
+
@mgr_logger.record_tagged_exception("Unexpected Error", e)
|
163
|
+
end
|
164
|
+
|
165
|
+
begin
|
166
|
+
if (@email_ready) then email_results(false) end
|
167
|
+
rescue => e
|
168
|
+
@mgr_logger.record_tagged_exception("Error emailing results", e)
|
169
|
+
end
|
170
|
+
|
171
|
+
@mgr_logger.close()
|
172
|
+
@mgr_logger = nil
|
173
|
+
end
|
174
|
+
|
175
|
+
$stderr = STDERR
|
176
|
+
$stdout = STDOUT
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# ====Description:
|
181
|
+
# This method runs the jobmanager application.
|
182
|
+
#
|
183
|
+
def run()
|
184
|
+
success = false
|
185
|
+
|
186
|
+
begin
|
187
|
+
FileUtils.mkdir_p(@config.job_logs_directory)
|
188
|
+
rescue => e
|
189
|
+
raise ComposedError.new("Error creating job_logs_directory", e)
|
190
|
+
end
|
191
|
+
|
192
|
+
job_log_file = File.join(@config.job_logs_directory, "#{@config.job_name}.log")
|
193
|
+
@mgr_logger.debug("Opening job log file #{job_log_file}.")
|
194
|
+
|
195
|
+
begin
|
196
|
+
job_log_file_stream = File.open(job_log_file, "w")
|
197
|
+
rescue => e
|
198
|
+
raise ComposedError.new("Error opening job log file", e)
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# The output of the job that will be invoked will be sent back
|
203
|
+
# to this process via a pipe (inside invoke_command). This
|
204
|
+
# process will use the job's output for 2 purposes. It will be
|
205
|
+
# forwarded to a job log file and the output will be used later
|
206
|
+
# when emailing results. invoke_command takes only 1
|
207
|
+
# job_output_stream. We could pass a string stream to
|
208
|
+
# invoke_command, and after write the contents to the job's log
|
209
|
+
# file, and also include it in the emailed results. However,
|
210
|
+
# this is less than optimal, as the job output will not be
|
211
|
+
# written to the log file in real time; it will not be written
|
212
|
+
# until after the job has completed. For long jobs, this can
|
213
|
+
# leave the job owner with no way of determining the status of
|
214
|
+
# the job while it is running. Instead, we construct a
|
215
|
+
# TeeStream which is a stream that forwards the messages to a
|
216
|
+
# list of streams. In this case, we will initialize the
|
217
|
+
# TeeStream to forward strings to the job log file stream and a
|
218
|
+
# string stream (which will be later used for emailing results).
|
219
|
+
#
|
220
|
+
begin
|
221
|
+
job_tee_stream = TeeStream.new(:file => job_log_file_stream,
|
222
|
+
:stringio => StringIO.new())
|
223
|
+
|
224
|
+
optional_args = {}
|
225
|
+
if @config.timeout then optional_args[:timeout] = @config.timeout end
|
226
|
+
optional_args[:command_path] = @config.command_path
|
227
|
+
|
228
|
+
# invoke the command - fork and exec the process, and wait til it finishes to reap the status.
|
229
|
+
success = System::invoke_command(@config.command,
|
230
|
+
job_tee_stream,
|
231
|
+
@mgr_logger,
|
232
|
+
optional_args)
|
233
|
+
|
234
|
+
job_output = job_tee_stream.get_stream(:stringio).string()
|
235
|
+
job_tee_stream.close()
|
236
|
+
|
237
|
+
rescue => e
|
238
|
+
@mgr_logger.record_tagged_exception("Error running job", e)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Rotate the job log file.
|
242
|
+
begin
|
243
|
+
if (File.file?(job_log_file))
|
244
|
+
|
245
|
+
before_entries = Dir.glob("#{@config.job_logs_directory}/#{@config.job_name}*")
|
246
|
+
|
247
|
+
logrotate_options = {
|
248
|
+
:count => @config.number_of_job_logs,
|
249
|
+
:date_time_ext => @config.date_time_extension,
|
250
|
+
:date_time_format => @config.date_time_extension_format,
|
251
|
+
:gzip => @config.zip_rotated_log_file
|
252
|
+
}
|
253
|
+
|
254
|
+
LogRotate.rotate_file(job_log_file, logrotate_options)
|
255
|
+
|
256
|
+
rotated_file = (Dir.glob("#{@config.job_logs_directory}/#{@config.job_name}*") - before_entries)[0]
|
257
|
+
@mgr_logger.info("Job log rotated to #{rotated_file}.")
|
258
|
+
end
|
259
|
+
rescue => e
|
260
|
+
success = false
|
261
|
+
@mgr_logger.record_tagged_exception("Error rotating file #{job_log_file}", e)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Email the results!
|
265
|
+
begin
|
266
|
+
if (@config.email_condition == :always ||
|
267
|
+
(@config.email_condition == :on_failure && !success))
|
268
|
+
|
269
|
+
email_results(success, job_output)
|
270
|
+
end
|
271
|
+
rescue => e
|
272
|
+
@mgr_logger.record_tagged_exception("Error emailing results", e)
|
273
|
+
end
|
274
|
+
|
275
|
+
rescue => e
|
276
|
+
handle_exception(e)
|
277
|
+
raise e
|
278
|
+
|
279
|
+
ensure
|
280
|
+
if (@mgr_logger) then
|
281
|
+
@mgr_logger.close()
|
282
|
+
@mgr_logger = nil
|
283
|
+
end
|
284
|
+
|
285
|
+
$stderr = STDERR
|
286
|
+
$stdout = STDOUT
|
287
|
+
end
|
288
|
+
|
289
|
+
private
|
290
|
+
|
291
|
+
#
|
292
|
+
# ====Description:
|
293
|
+
# This method reads the configuration file and the command line
|
294
|
+
# options into a configuration class (ApplicationConfig).
|
295
|
+
#
|
296
|
+
def gather_options(program_name, argv, config_file)
|
297
|
+
yaml_reader = nil
|
298
|
+
begin
|
299
|
+
yaml_reader = ConfigToolkit::YAMLReader.new(config_file)
|
300
|
+
rescue => e
|
301
|
+
raise ComposedError.new("Error loading #{config_file}", e)
|
302
|
+
end
|
303
|
+
|
304
|
+
command_parameters_hash = nil
|
305
|
+
begin
|
306
|
+
command_parameters_hash = JobManager::ApplicationOptionParser.parse(program_name, argv)
|
307
|
+
hash_reader = ConfigToolkit::HashReader.new(command_parameters_hash)
|
308
|
+
rescue => e
|
309
|
+
raise ComposedError.new("Error Parsing Command Line", e)
|
310
|
+
end
|
311
|
+
|
312
|
+
@config = nil
|
313
|
+
begin
|
314
|
+
# Union together the configuration file and the command line options.
|
315
|
+
override_reader = ConfigToolkit::OverrideReader.new(yaml_reader, hash_reader)
|
316
|
+
@config = JobManager::ApplicationConfig.load(override_reader)
|
317
|
+
rescue => e
|
318
|
+
raise ComposedError.new("Error validating configuration", e)
|
319
|
+
end
|
320
|
+
|
321
|
+
# if jobmanager is configured to email results, process email
|
322
|
+
# related configuration parameters.
|
323
|
+
if (@config.email_condition != :never)
|
324
|
+
|
325
|
+
# Do not email on error, email is not yet setup!
|
326
|
+
@config.email_settings_file = evaluate_configuration_parameter(@config, :email_settings_file)
|
327
|
+
|
328
|
+
# if the email_settings_file is a relative path, convert it to
|
329
|
+
# an absolute path (or a relative path relative to the
|
330
|
+
# process's current working directory). Assume that the
|
331
|
+
# email_settings_file is specified relative to the jobmanager
|
332
|
+
# configuration file's directory.
|
333
|
+
if (value = @config.email_settings_file)
|
334
|
+
if (!value.absolute?)
|
335
|
+
value = value.expand_path(File.dirname(config_file))
|
336
|
+
end
|
337
|
+
|
338
|
+
if (!value.file? || !value.readable?)
|
339
|
+
raise Error.new("email_settings_file (#{value}) does not exist or is not readable.\n")
|
340
|
+
end
|
341
|
+
|
342
|
+
@config.email_settings_file = value
|
343
|
+
end
|
344
|
+
|
345
|
+
# Note: From/To email addresses can be specified in either the
|
346
|
+
# jobmanager configuration file or the simpleemail gem
|
347
|
+
# configuration file.
|
348
|
+
begin
|
349
|
+
SimpleEmail::Email.email_settings_file = @config.email_settings_file
|
350
|
+
if (@config.email_from_address)
|
351
|
+
SimpleEmail::Email.default_from = @config.email_from_address
|
352
|
+
end
|
353
|
+
if (@config.email_to_address)
|
354
|
+
SimpleEmail::Email.default_to = @config.email_to_address
|
355
|
+
end
|
356
|
+
rescue => e
|
357
|
+
raise ComposedError.new("Error evaluating email settings file (#{@config.email_settings_file}, ", e)
|
358
|
+
end
|
359
|
+
|
360
|
+
@email_ready = true
|
361
|
+
end
|
362
|
+
|
363
|
+
if (@config.central_log_mode == :file && !@terminal_shell)
|
364
|
+
# Note: Earlier we set up @mgr_logger to log via syslog
|
365
|
+
# regardless of the central_log_mode configuration parameter
|
366
|
+
# (we hadn't read the configuration file at that point!).
|
367
|
+
# Close the syslog logger, and set @mgr_logger up to log
|
368
|
+
# directly to a file.
|
369
|
+
|
370
|
+
# Evaluate the string parameters that allow ERB syntax.
|
371
|
+
@config.central_log_file = evaluate_configuration_parameter(@config, :central_log_file)
|
372
|
+
|
373
|
+
begin
|
374
|
+
new_logger = ApplicationFileLogger.new(@config.central_log_file,
|
375
|
+
@user_name)
|
376
|
+
|
377
|
+
old_logger = @mgr_logger
|
378
|
+
@mgr_logger = new_logger
|
379
|
+
old_logger.close
|
380
|
+
|
381
|
+
rescue => e
|
382
|
+
raise ComposedError.new("Error Initializing Central Log", e)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
@mgr_logger.job_name = @config.job_name
|
387
|
+
@mgr_logger.level = @config.debug ? Logger::DEBUG : Logger::INFO
|
388
|
+
|
389
|
+
@config.job_logs_directory = evaluate_configuration_parameter(@config, :job_logs_directory)
|
390
|
+
end
|
391
|
+
|
392
|
+
#
|
393
|
+
# ====Description:
|
394
|
+
# This method emails the results of the job to the configured email address.
|
395
|
+
#
|
396
|
+
def email_results(success,
|
397
|
+
job_output = nil)
|
398
|
+
|
399
|
+
email_banner = "---------------------------------------------------------\n"
|
400
|
+
|
401
|
+
mgr_output = @mgr_logger.string()
|
402
|
+
|
403
|
+
# Evaluate the email_subject configuration parameter string with
|
404
|
+
# ERB. Resolve the following variables to their values listed
|
405
|
+
# below.
|
406
|
+
result = success ? "Success" : "Failure"
|
407
|
+
host_name = Sys::Host.hostname
|
408
|
+
job_name = @config.job_name
|
409
|
+
command = @config.command
|
410
|
+
user_name = @user_name
|
411
|
+
|
412
|
+
subject = nil
|
413
|
+
begin
|
414
|
+
subject = ERB.new(@config.email_subject).result(binding)
|
415
|
+
rescue => e
|
416
|
+
@mgr_logger.record_tagged_exception("Error evaluating email subject with ERB", e)
|
417
|
+
subject = "Invalid Subject - Check Logs!"
|
418
|
+
end
|
419
|
+
|
420
|
+
body =""
|
421
|
+
body += email_banner
|
422
|
+
body += "jobmanager Output:\n"
|
423
|
+
body += email_banner
|
424
|
+
body += "#{mgr_output}\n"
|
425
|
+
|
426
|
+
if (job_output && job_output.length > 0)
|
427
|
+
body += email_banner
|
428
|
+
body += "Job Output:\n"
|
429
|
+
body += email_banner
|
430
|
+
body += "#{job_output}\n"
|
431
|
+
end
|
432
|
+
|
433
|
+
ActionMailer::Base.logger = @mgr_logger
|
434
|
+
SimpleEmail::Email.send_email(nil, nil, subject, body)
|
435
|
+
end
|
436
|
+
|
437
|
+
def evaluate_configuration_parameter(config, parameter)
|
438
|
+
# Evaluate the parameter string with ERB, replacing the
|
439
|
+
# user_name variable reference with the user name that invoked
|
440
|
+
# this application.
|
441
|
+
evaluated_parameter = nil
|
442
|
+
|
443
|
+
begin
|
444
|
+
user_name = @user_name
|
445
|
+
evaluated_parameter = ERB.new(config.send(parameter)).result(binding)
|
446
|
+
rescue => e
|
447
|
+
raise ComposedError.new("Error evaluating #{parameter} with ERB", e)
|
448
|
+
end
|
449
|
+
|
450
|
+
return evaluated_parameter
|
451
|
+
end
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|