backup 3.5.1 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc2e8837fa998e2998728d7068b7b6bd40944aad
4
- data.tar.gz: 6d1f80d16e3de75e4e5ac424d261a4945b276114
3
+ metadata.gz: d68dc83802f92387efeb5d0bfd6e89c7d5d32e49
4
+ data.tar.gz: a9c414e7eb6a1c329b49329d3c4fc5391ab78f84
5
5
  SHA512:
6
- metadata.gz: 8a01458dadc536160bb9cbcb5b91cae1d0b4e3711b259c5a6d0af7a250e146cb7aeaa60549a66d986f4eeb3da4ce32e43d241740cabba831e2b1045eccec517e
7
- data.tar.gz: d4f84b2d26e477ade4d163be027ce3025ce3078eaa868d84a25c10c70a4b513afb8f12968077b1c272f514a872f1cd976b66638774a0ab3dcd1fdddc903034fd
6
+ metadata.gz: 1f801689681baae58593843de6828116bc688ece381912e47d6b74e8354f61bd437a13e0b36bf106331124de108788c626dd0fa1d16e09f9c04fff4834c9a747
7
+ data.tar.gz: ed9473697e3a81910ada2857f7c3533b341bc8ff0a9a6863db58d16cc408a2e29042ab4284bf0e44979dea0fa7dfebe68e48de10cc8b6a41a7c13ed62a321c1d
data/lib/backup/cli.rb CHANGED
@@ -156,15 +156,38 @@ module Backup
156
156
  exit(3)
157
157
  end
158
158
 
159
- # Model#perform! handles all exceptions from this point,
160
- # as each model may fail and return here to allow others to run.
161
- warnings = errors = false
162
- models.each do |model|
159
+ until models.empty?
160
+ model = models.shift
163
161
  model.perform!
164
- warnings ||= Logger.has_warnings?
165
- errors ||= Logger.has_errors?
162
+
163
+ case model.exit_status
164
+ when 1
165
+ warnings = true
166
+ when 2
167
+ errors = true
168
+ unless models.empty?
169
+ Logger.info Errors::ModelError.new(<<-EOS)
170
+ Backup will now continue...
171
+ The following triggers will now be processed:
172
+ (#{ models.map {|m| m.trigger }.join(', ') })
173
+ EOS
174
+ end
175
+ when 3
176
+ fatal = true
177
+ unless models.empty?
178
+ Logger.error Errors::ModelFatalError.new(<<-EOS)
179
+ Backup will now exit.
180
+ The following triggers will not be processed:
181
+ (#{ models.map {|m| m.trigger }.join(', ') })
182
+ EOS
183
+ end
184
+ end
185
+
186
+ model.notifiers.each(&:perform!)
187
+ exit(3) if fatal
166
188
  Logger.clear!
167
189
  end
190
+
168
191
  exit(errors ? 2 : 1) if errors || warnings
169
192
  end
170
193
 
data/lib/backup/errors.rb CHANGED
@@ -5,13 +5,21 @@ module Backup
5
5
  # - automatically defines module namespaces referenced under Backup::Errors
6
6
  # - any constant name referenced that ends with 'Error' will be created
7
7
  # as a subclass of Backup::Errors::Error
8
+ # - any constant name referenced that ends with 'FatalError' will be created
9
+ # as a subclass of Backup::Errors::FatalError
10
+ #
8
11
  # e.g.
9
12
  # err = Backup::Errors::Foo::Bar::FooError.new('error message')
10
13
  # err.message => "Foo::Bar::FooError: error message"
11
14
  #
15
+ # err = Backup::Errors::Foo::Bar::FooFatalError.new('error message')
16
+ # err.message => "Foo::Bar::FooFatalError: error message"
17
+ #
12
18
  module ErrorsHelper
13
19
  def const_missing(const)
14
- if const.to_s.end_with?('Error')
20
+ if const.to_s.end_with?('FatalError')
21
+ module_eval("class #{const} < Backup::Errors::FatalError; end")
22
+ elsif const.to_s.end_with?('Error')
15
23
  module_eval("class #{const} < Backup::Errors::Error; end")
16
24
  else
17
25
  module_eval("module #{const}; extend Backup::ErrorsHelper; end")
@@ -20,104 +28,61 @@ module Backup
20
28
  end
21
29
  end
22
30
 
23
- ##
24
- # provides cascading errors with formatted messages
25
- # see the specs for details
26
- #
27
- # e.g.
28
- # module Backup
29
- # begin
30
- # begin
31
- # begin
32
- # raise Errors::ZoneAError, 'an error occurred in Zone A'
33
- # rescue => err
34
- # raise Errors::ZoneBError.wrap(err, <<-EOS)
35
- # an error occurred in Zone B
36
- #
37
- # the following error should give a reason
38
- # EOS
39
- # end
40
- # rescue => err
41
- # raise Errors::ZoneCError.wrap(err)
42
- # end
43
- # rescue => err
44
- # puts Errors::ZoneDError.wrap(err, 'an error occurred in Zone D')
45
- # end
46
- # end
47
- #
48
- # Outputs:
49
- # ZoneDError: an error occurred in Zone D
50
- # Reason: ZoneCError
51
- # ZoneBError: an error occurred in Zone B
52
- #
53
- # the following error should give a reason
54
- # Reason: ZoneAError
55
- # an error occurred in Zone A
56
- #
57
31
  module Errors
58
32
  extend ErrorsHelper
59
33
 
60
- class Error < StandardError
61
-
62
- def self.wrap(orig_err, msg = nil)
63
- new(msg, orig_err)
64
- end
34
+ # Provides cascading errors with formatted messages.
35
+ # See the specs for details.
36
+ module NestedExceptions
65
37
 
66
- def initialize(msg = nil, orig_err = nil)
67
- super(msg)
68
- set_backtrace(orig_err.backtrace) if @orig_err = orig_err
38
+ def self.included(klass)
39
+ klass.extend Module.new {
40
+ def wrap(wrapped_exception, msg = nil)
41
+ new(msg, wrapped_exception)
42
+ end
43
+ }
69
44
  end
70
45
 
71
- def to_s
72
- return @to_s if @to_s
73
- orig_to_s = super()
74
-
75
- if orig_to_s == self.class.to_s
76
- msg = orig_err_msg ?
77
- "#{orig_err_class}: #{orig_err_msg}" : orig_err_class
78
- else
79
- msg = format_msg(orig_to_s)
80
- msg << "\n Reason: #{orig_err_class}" + (orig_err_msg ?
81
- "\n #{orig_err_msg}" : ' (no message given)') if @orig_err
46
+ def initialize(obj = nil, wrapped_exception = nil)
47
+ @wrapped_exception = wrapped_exception
48
+ msg = (obj.respond_to?(:to_str) ? obj.to_str : obj.to_s).
49
+ gsub(/^ */, ' ').strip
50
+ msg = clean_name(self.class.name) + (msg.empty? ? '' : ": #{ msg }")
51
+
52
+ if wrapped_exception
53
+ msg << "\n--- Wrapped Exception ---\n"
54
+ class_name = clean_name(wrapped_exception.class.name)
55
+ msg << class_name + ': ' unless
56
+ wrapped_exception.message.start_with? class_name
57
+ msg << wrapped_exception.message
82
58
  end
83
59
 
84
- @to_s = msg ? msg_prefix + msg : class_name
85
- end
86
-
87
- private
88
-
89
- def msg_prefix
90
- @msg_prefix ||= class_name + ': '
60
+ super(msg)
61
+ set_backtrace(wrapped_exception.backtrace) if wrapped_exception
91
62
  end
92
63
 
93
- def orig_msg
94
- @orig_msg ||= to_s.sub(msg_prefix, '')
95
- end
64
+ def exception(obj = nil)
65
+ return self if obj.nil? || equal?(obj)
96
66
 
97
- def class_name
98
- @class_name ||= self.class.to_s.sub('Backup::Errors::', '')
67
+ ex = self.class.new(obj, @wrapped_exception)
68
+ ex.set_backtrace(backtrace) unless ex.backtrace
69
+ ex
99
70
  end
100
71
 
101
- def orig_err_class
102
- return unless @orig_err
72
+ private
103
73
 
104
- @orig_err_class ||= @orig_err.is_a?(Errors::Error) ?
105
- @orig_err.send(:class_name) : @orig_err.class.to_s
74
+ def clean_name(name)
75
+ name.sub(/^Backup::Errors::/, '')
106
76
  end
107
77
 
108
- def orig_err_msg
109
- return unless @orig_err
110
- return @orig_err_msg unless @orig_err_msg.nil?
78
+ end
111
79
 
112
- msg = @orig_err.is_a?(Errors::Error) ?
113
- @orig_err.send(:orig_msg) : @orig_err.to_s
114
- @orig_err_msg = (msg == orig_err_class) ?
115
- false : format_msg(msg)
116
- end
80
+ class Error < StandardError
81
+ include NestedExceptions
82
+ end
117
83
 
118
- def format_msg(msg)
119
- msg.gsub(/^ */, ' ').strip
120
- end
84
+ class FatalError < Exception
85
+ include NestedExceptions
121
86
  end
122
87
 
123
88
  end
data/lib/backup/model.rb CHANGED
@@ -34,35 +34,35 @@ module Backup
34
34
  attr_reader :label
35
35
 
36
36
  ##
37
- # The databases attribute holds an array of database objects
37
+ # Array of configured Database objects.
38
38
  attr_reader :databases
39
39
 
40
40
  ##
41
- # The archives attr_accessor holds an array of archive objects
41
+ # Array of configured Archive objects.
42
42
  attr_reader :archives
43
43
 
44
44
  ##
45
- # The notifiers attr_accessor holds an array of notifier objects
45
+ # Array of configured Notifier objects.
46
46
  attr_reader :notifiers
47
47
 
48
48
  ##
49
- # The storages attribute holds an array of storage objects
49
+ # Array of configured Storage objects.
50
50
  attr_reader :storages
51
51
 
52
52
  ##
53
- # The syncers attribute holds an array of syncer objects
53
+ # Array of configured Syncer objects.
54
54
  attr_reader :syncers
55
55
 
56
56
  ##
57
- # Holds the configured Compressor
57
+ # The configured Compressor, if any.
58
58
  attr_reader :compressor
59
59
 
60
60
  ##
61
- # Holds the configured Encryptor
61
+ # The configured Encryptor, if any.
62
62
  attr_reader :encryptor
63
63
 
64
64
  ##
65
- # Holds the configured Splitter
65
+ # The configured Splitter, if any.
66
66
  attr_reader :splitter
67
67
 
68
68
  ##
@@ -74,17 +74,31 @@ module Backup
74
74
  attr_reader :time
75
75
 
76
76
  ##
77
- # Takes a trigger, label and the configuration block.
78
- # After the instance has evaluated the configuration block
79
- # to configure the model, it will be appended to Model.all
77
+ # Result of this model's backup process.
78
+ #
79
+ # 0 = Job was successful
80
+ # 1 = Job was successful, but issued warnings
81
+ # 2 = Job failed, additional triggers may be performed
82
+ # 3 = Job failed, additional triggers will not be performed
83
+ attr_reader :exit_status
84
+
85
+ ##
86
+ # Exception raised by either a +before+ hook or one of the model's
87
+ # procedures that caused the model to fail. An exception raised by an
88
+ # +after+ hook would not be stored here. Therefore, it is possible for
89
+ # this to be +nil+ even if #exit_status is 2 or 3.
90
+ attr_reader :exception
91
+
80
92
  def initialize(trigger, label, &block)
81
93
  @trigger = trigger.to_s
82
94
  @label = label.to_s
83
95
  @package = Package.new(self)
84
96
 
85
- procedure_instance_variables.each do |variable|
86
- instance_variable_set(variable, Array.new)
87
- end
97
+ @databases = []
98
+ @archives = []
99
+ @storages = []
100
+ @notifiers = []
101
+ @syncers = []
88
102
 
89
103
  instance_eval(&block) if block_given?
90
104
 
@@ -96,31 +110,27 @@ module Backup
96
110
  end
97
111
 
98
112
  ##
99
- # Adds an archive to the array of archives
100
- # to store during the backup process
113
+ # Adds an Archive. Multiple Archives may be added to the model.
101
114
  def archive(name, &block)
102
115
  @archives << Archive.new(self, name, &block)
103
116
  end
104
117
 
105
118
  ##
106
- # Adds a database to the array of databases
107
- # to dump during the backup process
119
+ # Adds an Database. Multiple Databases may be added to the model.
108
120
  def database(name, database_id = nil, &block)
109
121
  @databases << get_class_from_scope(Database, name).
110
122
  new(self, database_id, &block)
111
123
  end
112
124
 
113
125
  ##
114
- # Adds a storage method to the array of storage
115
- # methods to use during the backup process
126
+ # Adds an Storage. Multiple Storages may be added to the model.
116
127
  def store_with(name, storage_id = nil, &block)
117
128
  @storages << get_class_from_scope(Storage, name).
118
129
  new(self, storage_id, &block)
119
130
  end
120
131
 
121
132
  ##
122
- # Adds a syncer method to the array of syncer
123
- # methods to use during the backup process
133
+ # Adds an Syncer. Multiple Syncers may be added to the model.
124
134
  def sync_with(name, syncer_id = nil, &block)
125
135
  ##
126
136
  # Warn user of DSL changes
@@ -147,29 +157,29 @@ module Backup
147
157
  end
148
158
 
149
159
  ##
150
- # Adds a notifier to the array of notifiers
151
- # to use during the backup process
160
+ # Adds an Notifier. Multiple Notifiers may be added to the model.
152
161
  def notify_by(name, &block)
153
162
  @notifiers << get_class_from_scope(Notifier, name).new(self, &block)
154
163
  end
155
164
 
156
165
  ##
157
- # Adds an encryptor to use during the backup process
166
+ # Adds an Encryptor. Only one Encryptor may be added to the model.
167
+ # This will be used to encrypt the final backup package.
158
168
  def encrypt_with(name, &block)
159
169
  @encryptor = get_class_from_scope(Encryptor, name).new(&block)
160
170
  end
161
171
 
162
172
  ##
163
- # Adds a compressor to use during the backup process
173
+ # Adds an Compressor. Only one Compressor may be added to the model.
174
+ # This will be used to compress each individual Archive and Database
175
+ # stored within the final backup package.
164
176
  def compress_with(name, &block)
165
177
  @compressor = get_class_from_scope(Compressor, name).new(&block)
166
178
  end
167
179
 
168
180
  ##
169
- # Adds a method that allows the user to configure this backup model
170
- # to use a Splitter, with the given +chunk_size+
171
- # The +chunk_size+ (in megabytes) will later determine
172
- # in how many chunks the backup needs to be split into
181
+ # Adds a Splitter with the given +chunk_size+ in MB.
182
+ # This will split the final backup package into multiple files.
173
183
  def split_into_chunks_of(chunk_size)
174
184
  if chunk_size.is_a?(Integer)
175
185
  @splitter = Splitter.new(self, chunk_size)
@@ -182,79 +192,97 @@ module Backup
182
192
  end
183
193
 
184
194
  ##
185
- # Performs the backup process
186
- ##
187
- # [Databases]
188
- # Runs all (if any) database objects to dump the databases
189
- ##
190
- # [Archives]
191
- # Runs all (if any) archive objects to package all their
192
- # paths in to a single tar file and places it in the backup folder
193
- ##
194
- # [Packaging]
195
- # After all the database dumps and archives are placed inside
196
- # the folder, it'll make a single .tar package (archive) out of it
197
- ##
198
- # [Encryption]
199
- # Optionally encrypts the packaged file with the configured encryptor
200
- ##
201
- # [Compression]
202
- # Optionally compresses the each Archive and Database dump with the configured compressor
203
- ##
204
- # [Splitting]
205
- # Optionally splits the backup file in to multiple smaller chunks before transferring them
206
- ##
207
- # [Storages]
208
- # Runs all (if any) storage objects to store the backups to remote locations
209
- # and (if configured) it'll cycle the files on the remote location to limit the
210
- # amount of backups stored on each individual location
211
- ##
212
- # [Syncers]
213
- # Runs all (if any) sync objects to store the backups to remote locations.
214
- # A Syncer does not go through the process of packaging, compressing, encrypting backups.
215
- # A Syncer directly transfers data from the filesystem to the remote location
195
+ # Defines a block of code to run before the model's procedures.
196
+ #
197
+ # Warnings logged within the before hook will elevate the model's
198
+ # exit_status to 1 and cause warning notifications to be sent.
199
+ #
200
+ # Raising an exception will abort the model and cause failure notifications
201
+ # to be sent. If the exception is a StandardError, exit_status will be 2.
202
+ # If the exception is not a StandardError, exit_status will be 3.
203
+ #
204
+ # If any exception is raised, any defined +after+ hook will be skipped.
205
+ def before(&block)
206
+ @before ||= block
207
+ end
208
+
216
209
  ##
217
- # [Notifiers]
218
- # Runs all (if any) notifier objects when a backup proces finished with or without
219
- # any errors.
210
+ # Defines a block of code to run after the model's procedures.
211
+ #
212
+ # This code is ensured to run, even if the model failed, **unless** a
213
+ # +before+ hook raised an exception and aborted the model.
214
+ #
215
+ # The code block will be passed the model's current exit_status:
216
+ #
217
+ # `0`: Success, no warnings.
218
+ # `1`: Success, but warnings were logged.
219
+ # `2`: Failure, but additional models/triggers will still be processed.
220
+ # `3`: Failure, no additional models/triggers will be processed.
221
+ #
222
+ # The model's exit_status may be elevated based on the after hook's
223
+ # actions, but will never be decreased.
224
+ #
225
+ # Warnings logged within the after hook may elevate the model's
226
+ # exit_status to 1 and cause warning notifications to be sent.
227
+ #
228
+ # Raising an exception may elevate the model's exit_status and cause
229
+ # failure notifications to be sent. If the exception is a StandardError,
230
+ # the exit_status will be elevated to 2. If the exception is not a
231
+ # StandardError, the exit_status will be elevated to 3.
232
+ def after(&block)
233
+ @after ||= block
234
+ end
235
+
220
236
  ##
221
- # [Cleaning]
222
- # Once the final Packaging is complete, the temporary folder used will be removed.
223
- # Then, once all Storages have run, the final packaged files will be removed.
224
- # If any errors occur during the backup process, all temporary files will be left in place.
225
- # If the error occurs before Packaging, then the temporary folder (tmp_path/trigger)
226
- # will remain and may contain all or some of the configured Archives and/or Database dumps.
227
- # If the error occurs after Packaging, but before the Storages complete, then the final
237
+ # Performs the backup process
238
+ #
239
+ # Once complete, #exit_status will indicate the result of this process.
240
+ #
241
+ # If any errors occur during the backup process, all temporary files will
242
+ # be left in place. If the error occurs before Packaging, then the
243
+ # temporary folder (tmp_path/trigger) will remain and may contain all or
244
+ # some of the configured Archives and/or Database dumps. If the error
245
+ # occurs after Packaging, but before the Storages complete, then the final
228
246
  # packaged files (located in the root of tmp_path) will remain.
229
- # *** Important *** If an error occurs and any of the above mentioned temporary files remain,
230
- # those files *** will be removed *** before the next scheduled backup for the same trigger.
231
247
  #
248
+ # *** Important ***
249
+ # If an error occurs and any of the above mentioned temporary files remain,
250
+ # those files *** will be removed *** before the next scheduled backup for
251
+ # the same trigger.
232
252
  def perform!
233
253
  @started_at = Time.now
234
254
  @time = package.time = @started_at.strftime("%Y.%m.%d.%H.%M.%S")
235
- log!(:started)
236
255
 
237
- prepare!
256
+ log!(:started)
257
+ before_hook
238
258
 
239
- if databases.any? or archives.any?
240
- procedures.each do |procedure|
241
- (procedure.call; next) if procedure.is_a?(Proc)
242
- procedure.each(&:perform!)
243
- end
259
+ procedures.each do |procedure|
260
+ procedure.is_a?(Proc) ? procedure.call : procedure.each(&:perform!)
244
261
  end
245
262
 
246
263
  syncers.each(&:perform!)
247
- notifiers.each(&:perform!)
248
- log!(:finished)
249
264
 
250
265
  rescue Exception => err
251
- log!(:failure, err)
252
- send_failure_notifications
253
- exit(3) unless err.is_a?(StandardError)
266
+ @exception = err
267
+
268
+ ensure
269
+ set_exit_status
270
+ log!(:finished)
271
+ after_hook
254
272
  end
255
273
 
256
274
  private
257
275
 
276
+ ##
277
+ # Returns an array of procedures that will be performed if any
278
+ # Archives or Databases are configured for the model.
279
+ def procedures
280
+ return [] unless databases.any? || archives.any?
281
+
282
+ [lambda { prepare! }, databases, archives,
283
+ lambda { package! }, storages, lambda { clean! }]
284
+ end
285
+
258
286
  ##
259
287
  # Clean any temporary files and/or package files left over
260
288
  # from the last time this model/trigger was performed.
@@ -280,18 +308,6 @@ module Backup
280
308
  Cleaner.remove_package(package)
281
309
  end
282
310
 
283
- ##
284
- # Returns an array of procedures
285
- def procedures
286
- [databases, archives, lambda { package! }, storages, lambda { clean! }]
287
- end
288
-
289
- ##
290
- # Returns an Array of the names (String) of the procedure instance variables
291
- def procedure_instance_variables
292
- [:@databases, :@archives, :@storages, :@notifiers, :@syncers]
293
- end
294
-
295
311
  ##
296
312
  # Returns the class/model specified by +name+ inside of +scope+.
297
313
  # +scope+ should be a Class/Module.
@@ -319,43 +335,84 @@ module Backup
319
335
  end
320
336
 
321
337
  ##
322
- # Logs messages when the backup starts, finishes or fails
323
- def log!(action, exception = nil)
338
+ # Sets or updates the model's #exit_status.
339
+ def set_exit_status
340
+ @exit_status = if exception
341
+ exception.is_a?(StandardError) ? 2 : 3
342
+ else
343
+ Logger.has_warnings? ? 1 : 0
344
+ end
345
+ end
346
+
347
+ ##
348
+ # Runs the +before+ hook.
349
+ # Any exception raised will be wrapped and re-raised, where it will be
350
+ # handled by #perform the same as an exception raised while performing
351
+ # the model's #procedures. Only difference is that an exception raised
352
+ # here will prevent any +after+ hook from being run.
353
+ def before_hook
354
+ return unless before
355
+
356
+ Logger.info 'Before Hook Starting...'
357
+ before.call
358
+ Logger.info 'Before Hook Finished.'
359
+
360
+ rescue Exception => err
361
+ @before_hook_failed = true
362
+ ex = err.is_a?(StandardError) ?
363
+ Errors::Model::HookError : Errors::Model::HookFatalError
364
+ raise ex.wrap(err, 'Before Hook Failed!')
365
+ end
366
+
367
+ ##
368
+ # Runs the +after+ hook.
369
+ # Any exception raised here will be logged only and the model's
370
+ # #exit_status will be elevated if neccessary.
371
+ def after_hook
372
+ return unless after && !@before_hook_failed
373
+
374
+ Logger.info 'After Hook Starting...'
375
+ after.call(exit_status)
376
+ Logger.info 'After Hook Finished.'
377
+
378
+ set_exit_status # in case hook logged warnings
379
+
380
+ rescue Exception => err
381
+ fatal = !err.is_a?(StandardError)
382
+ ex = fatal ? Errors::Model::HookFatalError : Errors::Model::HookError
383
+ Logger.error ex.wrap(err, 'After Hook Failed!')
384
+ # upgrade exit_status if needed
385
+ (@exit_status = fatal ? 3 : 2) unless exit_status == 3
386
+ end
387
+
388
+ ##
389
+ # Logs messages when the model starts and finishes.
390
+ #
391
+ # #exception will be set here if #exit_status is > 1,
392
+ # since log(:finished) is called before the +after+ hook.
393
+ def log!(action)
324
394
  case action
325
395
  when :started
326
- Logger.info "Performing Backup for '#{label} (#{trigger})'!\n" +
396
+ Logger.info "Performing Backup for '#{ label } (#{ trigger })'!\n" +
327
397
  "[ backup #{ VERSION } : #{ RUBY_DESCRIPTION } ]"
328
398
 
329
399
  when :finished
330
- msg = "Backup for '#{ label } (#{ trigger })' " +
331
- "Completed %s in #{ elapsed_time }"
332
- if Logger.has_warnings?
333
- Logger.warn msg % 'Successfully (with Warnings)'
334
- else
335
- Logger.info msg % 'Successfully'
336
- end
337
-
338
- when :failure
339
- err = Errors::ModelError.wrap(exception, <<-EOS)
340
- Backup for #{label} (#{trigger}) Failed!
341
- An Error occured which has caused this Backup to abort before completion.
342
- EOS
343
- Logger.error err
344
- Logger.error "\nBacktrace:\n\s\s" + err.backtrace.join("\n\s\s") + "\n\n"
400
+ if exit_status > 1
401
+ ex = exit_status == 2 ? Errors::ModelError : Errors::ModelFatalError
402
+ err = ex.wrap(exception, "Backup for #{ label } (#{ trigger }) Failed!")
403
+ Logger.error err
404
+ Logger.error "\nBacktrace:\n\s\s" + err.backtrace.join("\n\s\s") + "\n\n"
345
405
 
346
- Cleaner.warnings(self)
347
-
348
- if exception.is_a?(StandardError)
349
- Logger.info Errors::ModelError.new(<<-EOS)
350
- If you have other Backup jobs (triggers) configured to run,
351
- Backup will now attempt to continue...
352
- EOS
406
+ Cleaner.warnings(self)
353
407
  else
354
- Logger.error Errors::ModelError.new(<<-EOS)
355
- This Error was Fatal and Backup will now exit.
356
- If you have other Backup jobs (triggers) configured to run,
357
- they will not be processed.
358
- EOS
408
+ msg = "Backup for '#{ label } (#{ trigger })' "
409
+ if exit_status == 1
410
+ msg << "Completed Successfully (with Warnings) in #{ elapsed_time }"
411
+ Logger.warn msg
412
+ else
413
+ msg << "Completed Successfully in #{ elapsed_time }"
414
+ Logger.info msg
415
+ end
359
416
  end
360
417
  end
361
418
  end
@@ -371,21 +428,5 @@ module Backup
371
428
  '%02d:%02d:%02d' % [hours, minutes, seconds]
372
429
  end
373
430
 
374
- ##
375
- # Sends notifications when a backup fails.
376
- # Errors are logged and rescued, since the error that caused the
377
- # backup to fail could have been an error with a notifier.
378
- def send_failure_notifications
379
- notifiers.each do |n|
380
- begin
381
- n.perform!(true)
382
- rescue Exception => err
383
- Logger.error Errors::ModelError.wrap(err, <<-EOS)
384
- #{ n.class } Failed to send notification of backup failure.
385
- EOS
386
- end
387
- end
388
- end
389
-
390
431
  end
391
432
  end