yore 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -2,3 +2,15 @@
2
2
 
3
3
  * 1 major enhancement:
4
4
  * Initial release
5
+
6
+ == 0.0.2 2009-2-04
7
+
8
+ * now using popen4
9
+ * raises exceptions when commandline tools fail
10
+ * now using shoulda for tests
11
+
12
+ == 0.0.3 2009-02-20
13
+
14
+ * much better logging, reporting and console output
15
+
16
+
data/Manifest.txt CHANGED
@@ -3,8 +3,11 @@ History.txt
3
3
  lib/ihl_ruby/enum.rb
4
4
  lib/ihl_ruby/extend_base_classes.rb
5
5
  lib/ihl_ruby/misc_utils.rb
6
+ lib/ihl_ruby/config.rb
7
+ lib/ihl_ruby/logging.rb
6
8
  lib/ihl_ruby/string_utils.rb
7
9
  lib/ihl_ruby/xml_utils.rb
10
+ lib/ihl_ruby/shell_extras.rb
8
11
  lib/yore.orig.rb
9
12
  lib/yore/yore_core.rb
10
13
  Manifest.txt
data/bin/yore CHANGED
@@ -24,9 +24,10 @@ def command(aParser,aController,aAction,aShortDescription=nil,aOptionParser=nil,
24
24
  c.options = aOptionParser if aOptionParser
25
25
  c.set_execution_block do |args|
26
26
  job = args.first
27
+ aController.logger.info "Job file: #{File.expand_path(job)}"
27
28
  xmlRoot = XmlUtils.get_file_root(job)
28
- aController.configure(xmlRoot,CMD_OPTIONS,{:basepath => File.dirname(File.expand_path(job))})
29
- aController.send(aAction,args)
29
+ aController.configure(xmlRoot,CMD_OPTIONS,{:basepath => File.dirname(File.expand_path(job))})
30
+ aController.do_action(aAction,args)
30
31
  end
31
32
  aParser.add_command(c)
32
33
  end
@@ -63,3 +64,6 @@ command(cmd,yore,:db_dump,"Dump database by name in job\n")
63
64
 
64
65
  cmd.parse
65
66
 
67
+ yore.logger.info "\nComplete.\n"
68
+ yore.report
69
+
@@ -0,0 +1,107 @@
1
+ class ConfigClass < Hash
2
+
3
+ attr_reader :default_values
4
+
5
+ def initialize(aDefaultValues,aNewValues=nil,&aBlock)
6
+ self.merge!(@default_values = aDefaultValues.clone)
7
+ if aNewValues
8
+ block_given? ? read(aNewValues,&aBlock) : read(aNewValues)
9
+ end
10
+ end
11
+
12
+ # aBlock allows values to be filtered based on key,default and new values
13
+ def read(aSource,&aBlock)
14
+ default_values.each do |k,v|
15
+ done = false
16
+ if block_given? && ((newv = yield(k,v,aSource && aSource[k])) != nil)
17
+ self[k] = newv
18
+ done = true
19
+ end
20
+ copy_item(aSource,k) if !done && aSource && aSource[k]
21
+ end
22
+ self
23
+ end
24
+
25
+ # reset values back to defaults
26
+ def reset
27
+ self.clear
28
+ self.merge!(default_values)
29
+ end
30
+
31
+ def set_int(aKey,aValue)
32
+ case aValue
33
+ when String then self[aKey] = aValue.to_integer(self[aKey]);
34
+ when Fixnum then self[aKey] = aValue;
35
+ when Float then self[aKey] = aValue.to_i;
36
+ end
37
+ end
38
+
39
+ def set_float(aKey,aValue)
40
+ case aValue
41
+ when String then self[aKey] = aValue.to_float(self[aKey]);
42
+ when Fixnum then self[aKey] = aValue.to_f;
43
+ when Float then self[aKey] = aValue;
44
+ end
45
+ end
46
+
47
+ def set_boolean(aKey,aValue)
48
+ case aValue
49
+ when TrueClass,FalseClass then self[aKey] = aValue;
50
+ when String then self[aKey] = (['1','yes','y','true','on'].include?(aValue.downcase))
51
+ else
52
+ set_boolean(aKey,aValue.to_s)
53
+ end
54
+ end
55
+
56
+ def set_symbol(aKey,aValue)
57
+ case aValue
58
+ when String then self[aKey] = (aValue.to_sym rescue nil);
59
+ when Symbol then self[aKey] = aValue;
60
+ end
61
+ end
62
+
63
+ def copy_item(aHash,aKey)
64
+ case default_values[aKey]
65
+ when NilClass then ;
66
+ when String then self[aKey] = aHash[aKey].to_s unless aHash[aKey].nil?
67
+ when Float then set_float(aKey,aHash[aKey]);
68
+ when Fixnum then set_int(aKey,aHash[aKey]);
69
+ when TrueClass, FalseClass then set_boolean(aKey,aHash[aKey]);
70
+ when Symbol then self[aKey] = (aHash[aKey].to_sym rescue nil)
71
+ else
72
+ raise Error.new('unsupported type')
73
+ end
74
+ end
75
+
76
+ def copy_strings(aHash,*aKeys)
77
+ aKeys.each do |k|
78
+ self[k] = aHash[k].to_s unless aHash[k].nil?
79
+ end
80
+ end
81
+
82
+ def copy_ints(*aDb)
83
+ aHash = aDb.shift
84
+ aKeys = aDb
85
+ aKeys.each do |k|
86
+ set_int(k,aHash[k])
87
+ end
88
+ end
89
+
90
+ def copy_floats(aHash,*aKeys)
91
+ aKeys.each do |k|
92
+ set_float(k,aHash[k])
93
+ end
94
+ end
95
+
96
+ def copy_booleans(aHash,*aKeys)
97
+ aKeys.each do |k|
98
+ set_boolean(k,aHash[k])
99
+ end
100
+ end
101
+
102
+ def to_hash
103
+ {}.merge(self)
104
+ end
105
+
106
+ end
107
+
@@ -0,0 +1,159 @@
1
+ require 'logger'
2
+ require 'ihl_ruby/misc_utils'
3
+
4
+ class Logger
5
+ attr_reader :logdev
6
+ end
7
+
8
+ module LogUtils
9
+
10
+ # eg.
11
+ # {
12
+ # 'destination' => 'STDERR|STDOUT|FILE',
13
+ # 'filename' => '/path/to/file.ext',
14
+ # 'level' => 'DEBUG|INFO|...',
15
+ #
16
+ # 'age' = 'daily|weekly|monthly',
17
+ # OR
18
+ # 'max_files' => 3,
19
+ # 'max_bytes' => 1024000
20
+ # }
21
+ def self.create_logger_from_config(aConfigHash)
22
+ if not aConfigHash
23
+ result = Logger.new(STDERR)
24
+ result.level = Logger::INFO
25
+ return result
26
+ end
27
+
28
+ result = nil
29
+ case aConfigHash['destination']
30
+ when 'STDERR' then
31
+ result = Logger.new(STDERR)
32
+ when 'STDOUT' then
33
+ result = Logger.new(STDOUT)
34
+ when 'FILE' then
35
+ result = aConfigHash['age'] ?
36
+ Logger.new(aConfigHash['filename'],aConfigHash['age']) :
37
+ Logger.new(
38
+ aConfigHash['filename'],
39
+ (aConfigHash['max_files'] || 3).to_i,
40
+ (aConfigHash['max_bytes'] || 1024000).to_i
41
+ )
42
+ else
43
+ result = Logger.new(STDERR)
44
+ end
45
+ puts valstr = "Logger::#{(aConfigHash['level'] || 'INFO').upcase}"
46
+ result.level = eval(valstr)
47
+ return result
48
+ end
49
+
50
+ # use this to trunc a log file to 0 bytes
51
+ def self.trunc(aFilename)
52
+ f = File.open(aFilename, "w")
53
+ f.close
54
+ end
55
+
56
+ class ReportFormatter < Logger::Formatter
57
+ def call(severity, time, progname, msg)
58
+ "|%s %1s %s\n" % [(time.strftime('%H%M%S.')<<"%03d" % (time.usec/1000)),severity[0..0],msg2str(msg)]
59
+ end
60
+ end
61
+
62
+ class Reporter < Logger
63
+ def initialize(logdev)
64
+ super(logdev)
65
+ end
66
+
67
+ end
68
+
69
+ def self.create_reporter(aFilename=nil)
70
+ aFilename ||= MiscUtils::temp_file()
71
+ result = Logger.new(aFilename)
72
+ result.formatter = ReportFormatter.new
73
+ result
74
+ end
75
+ end
76
+
77
+
78
+
79
+ class MultiLogger < Logger
80
+
81
+ attr_reader :loggers
82
+
83
+ def initialize(aLoggers)
84
+ @loggers = aLoggers.is_a?(Array) ? aLoggers : [aLoggers]
85
+ end
86
+
87
+ def add(severity, message = nil, progname = nil, &block)
88
+ return true if !@loggers
89
+ severity ||= UNKNOWN
90
+ @loggers.each do |lr|
91
+ block_given? ? lr.add(severity,message,progname,&block) : lr.add(severity,message,progname)
92
+ end
93
+ true
94
+ end
95
+ alias log add
96
+
97
+ def <<(msg)
98
+ @loggers.each do |lr|
99
+ lr << msg
100
+ end
101
+ end
102
+
103
+ def close
104
+ @loggers.each do |lr|
105
+ lr.close
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ #DEBUG D
112
+ #INFO
113
+ #WARN ?
114
+ #ERROR !
115
+ #FATAL F
116
+ #UNKNOWN U
117
+
118
+ # Logger that mostly works like a STDOUT logger, except that warnings and above get sent to STDERR instead
119
+ class ConsoleLogger < Logger
120
+
121
+ class ReportFormatter < Logger::Formatter
122
+ def call(severity, time, progname, msg)
123
+ msg2str(msg)+"\n"
124
+ end
125
+ end
126
+
127
+ def initialize(aErrLevel = Severity::WARN)
128
+ super(STDOUT)
129
+ self.formatter = ReportFormatter.new
130
+ self.level = Severity::INFO
131
+ self << "\n"
132
+ @err_logger = Logger.new(STDERR)
133
+ @err_level = aErrLevel
134
+ @err_logger.formatter = ReportFormatter.new
135
+ end
136
+
137
+ alias_method :orig_add, :add
138
+ def add(severity, message = nil, progname = nil, &block)
139
+ if severity >= @err_level
140
+ block_given? ? @err_logger.add(severity,message,progname,&block) : @err_logger.add(severity,message,progname)
141
+ else
142
+ block_given? ? orig_add(severity,message,progname,&block) : orig_add(severity,message,progname)
143
+ end
144
+ end
145
+ alias log add
146
+
147
+ #
148
+ # Close the logging device.
149
+ #
150
+ def close
151
+ begin
152
+ @logdev.close if @logdev
153
+ ensure
154
+ @err_logger.close
155
+ end
156
+ end
157
+
158
+ end
159
+
@@ -2,6 +2,8 @@ require 'tmpdir'
2
2
  require 'logger'
3
3
  require 'pathname'
4
4
 
5
+ require 'ihl_ruby/logging'
6
+
5
7
  module MiscUtils
6
8
 
7
9
  def self.logger
@@ -346,81 +348,6 @@ END_OF_MESSAGE
346
348
 
347
349
  end
348
350
 
349
- class Logger
350
- attr_reader :logdev
351
- end
352
-
353
- module LogUtils
354
-
355
- # eg.
356
- # {
357
- # 'destination' => 'STDERR|STDOUT|FILE',
358
- # 'filename' => '/path/to/file.ext',
359
- # 'level' => 'DEBUG|INFO|...',
360
- #
361
- # 'age' = 'daily|weekly|monthly',
362
- # OR
363
- # 'max_files' => 3,
364
- # 'max_bytes' => 1024000
365
- # }
366
- def self.create_logger_from_config(aConfigHash)
367
- if not aConfigHash
368
- result = Logger.new(STDERR)
369
- result.level = Logger::DEBUG
370
- return result
371
- end
372
-
373
- result = nil
374
- case aConfigHash['destination']
375
- when 'STDERR' then
376
- result = Logger.new(STDERR)
377
- when 'STDOUT' then
378
- result = Logger.new(STDOUT)
379
- when 'FILE' then
380
- result = aConfigHash['age'] ?
381
- Logger.new(aConfigHash['filename'],aConfigHash['age']) :
382
- Logger.new(
383
- aConfigHash['filename'],
384
- (aConfigHash['max_files'] || 3).to_i,
385
- (aConfigHash['max_bytes'] || 1024000).to_i
386
- )
387
- else
388
- result = Logger.new(STDERR)
389
- end
390
- puts valstr = "Logger::#{(aConfigHash['level'] || 'INFO').upcase}"
391
- result.level = eval(valstr)
392
- return result
393
- end
394
-
395
- # use this to trunc a log file to 0 bytes
396
- def self.trunc(aFilename)
397
- f = File.open(aFilename, "w")
398
- f.close
399
- end
400
-
401
- class ReportFormatter < Logger::Formatter
402
- def call(severity, time, progname, msg)
403
- "|%s %1s %s\n" % [(time.strftime('%H%M%S.')<<"%03d" % (time.usec/1000)),severity[0..0],msg2str(msg)]
404
- end
405
- end
406
-
407
- class Reporter < Logger
408
- def initialize(logdev)
409
- super(logdev)
410
- end
411
-
412
- end
413
-
414
- def self.create_reporter(aFilename=nil)
415
- aFilename ||= MiscUtils::temp_file()
416
- result = Logger.new(aFilename)
417
- result.formatter = ReportFormatter.new
418
- result
419
- end
420
- end
421
-
422
-
423
-
424
351
  # include this at the top of a class to protect it from baddies.
425
352
  # eg.
426
353
  # + nearly all ancestor public_instance_methods will be hidden
@@ -0,0 +1,84 @@
1
+ require 'rubygems'
2
+ gem 'RequirePaths'; require 'require_paths'
3
+ require_paths '.','..'
4
+
5
+ gem 'Platform'; require 'platform'
6
+ gem 'shairontoledo-popen4'; require 'popen4'
7
+
8
+ module POpen4
9
+
10
+ class ExecuteError < StandardError
11
+
12
+ attr_reader :result #,:stderr,:stdout,:exitcode,:pid
13
+
14
+ def initialize(aArg)
15
+ if aArg.is_a? Hash
16
+ msg = ([aArg[:stderr],aArg[:stdout],"Error #{aArg[:exitcode].to_s}"].find {|i| i && !i.empty?})
17
+ super(msg)
18
+ @result = aArg
19
+ else
20
+ super(aArg)
21
+ end
22
+ end
23
+
24
+ def inspect
25
+ "#{self.class.to_s}: #{@result.inspect}"
26
+ end
27
+
28
+ end
29
+
30
+ def self.pump_thread(aIn,aOut)
31
+ Thread.new do
32
+ loop { aOut.puts aIn.gets }
33
+ end
34
+ end
35
+
36
+ # Usage :
37
+ # result = POpen4::shell('somebinary') do |r| # block gives opportunity to adjust result, and avoid exception raised from non-zero exit codes
38
+ # if r[:exitcode]==254 # eg. say this binary returns 254 to mean something special but not an error
39
+ # r[:stdout] = 'some correct output'
40
+ # r[:stderr] = ''
41
+ # r[:exitcode] = 0
42
+ # end
43
+ # end
44
+ #
45
+ # OR
46
+ #
47
+ # result = POpen4::shell('somebinary');
48
+ # puts result[:stdout]
49
+ #
50
+ # Giving aStdOut,aStdErr causes the command output to be connected to the given stream, and that stream to not be given in the result hash
51
+ def self.shell(aCommand,aWorkingDir=nil,aTimeout=nil,aStdOut=nil,aStdErr=nil)
52
+ raise ExecuteError.new('aWorkingDir doesnt exist') unless !aWorkingDir || File.exists?(aWorkingDir)
53
+ orig_wd = Dir.getwd
54
+ result = {:command => aCommand, :dir => (aWorkingDir || orig_wd)}
55
+ status = nil
56
+ begin
57
+ Dir.chdir(aWorkingDir) if aWorkingDir
58
+ Timeout.timeout(aTimeout,ExecuteError) do # nil aTimeout will not time out
59
+ status = POpen4::popen4(aCommand) do |stdout, stderr, stdin, pid|
60
+ thrOut = aStdOut ? Thread.new { aStdOut.puts stdout.read } : nil
61
+ thrErr = aStdErr ? Thread.new { aStdErr.puts stderr.read } : nil
62
+ thrOut.join if thrOut
63
+ thrErr.join if thrErr
64
+
65
+ result[:stdout] = stdout.read unless aStdOut
66
+ result[:stderr] = stderr.read unless aStdErr
67
+ result[:pid] = pid
68
+ end
69
+ end
70
+ ensure
71
+ Dir.chdir(orig_wd)
72
+ end
73
+ result[:exitcode] = (status && status.exitstatus) || 1
74
+ yield(result) if block_given?
75
+ raise ExecuteError.new(result) if result[:exitcode] != 0
76
+ return result
77
+ end
78
+
79
+ def self.shell_out(aCommand,aWorkingDir=nil,aTimeout=nil,&block)
80
+ block_given? ? POpen4::shell(aCommand,aWorkingDir,aTimeout,STDOUT,STDERR,&block) : POpen4::shell(aCommand,aWorkingDir,aTimeout,STDOUT,STDERR)
81
+ end
82
+
83
+ end
84
+
data/lib/yore.orig.rb CHANGED
@@ -2,5 +2,5 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  module Yore
5
- VERSION = '0.0.2'
5
+ VERSION = '0.0.3'
6
6
  end
@@ -2,78 +2,21 @@ require 'rubygems'
2
2
  gem 'RequirePaths'; require 'require_paths'
3
3
  require_paths '.','..'
4
4
 
5
- gem 'Platform'; require 'platform'
6
- gem 'shairontoledo-popen4'; require 'popen4'
7
5
 
8
6
  require 'fileutils'
9
7
  require 'net/smtp'
10
8
 
11
9
  require 'ihl_ruby/misc_utils'
10
+ require 'ihl_ruby/logging'
12
11
  require 'ihl_ruby/string_utils'
13
12
  require 'ihl_ruby/xml_utils'
14
13
  require 'ihl_ruby/extend_base_classes'
14
+ require 'ihl_ruby/shell_extras'
15
+ require 'ihl_ruby/config'
15
16
 
16
17
  THIS_FILE = __FILE__
17
18
  THIS_DIR = File.dirname(THIS_FILE)
18
19
 
19
- module POpen4
20
-
21
- class ExecuteError < StandardError
22
-
23
- attr_reader :result #,:stderr,:stdout,:exitcode,:pid
24
-
25
- def initialize(aArg)
26
- if aArg.is_a? Hash
27
- super(aArg[:stdout] || '')
28
- @result = aArg
29
- # @stderr = aArg[:stderr]
30
- # @stdout = aArg[:stdout]
31
- # @exitcode = aArg[:exitcode]
32
- # @pid = aArg[:pid]
33
- else
34
- super(aArg)
35
- end
36
- end
37
-
38
- end
39
-
40
- # Usage :
41
- # result = POpen4::shell('somebinary') do |r| # block gives opportunity to adjust result, and avoid exception raised from non-zero exit codes
42
- # if r[:exitcode]==254 # eg. say this binary returns 254 to mean something special but not an error
43
- # r[:stdout] = 'some correct output'
44
- # r[:stderr] = ''
45
- # r[:exitcode] = 0
46
- # end
47
- # end
48
- #
49
- # OR
50
- #
51
- # result = POpen4::shell('somebinary');
52
- # puts result[:stdout]
53
- def self.shell(aCommand,aWorkingDir=nil,aTimeout=nil)
54
- raise ExecuteError.new('aWorkingDir doesnt exist') unless !aWorkingDir || File.exists?(aWorkingDir)
55
- orig_wd = Dir.getwd
56
- result = {:command => aCommand, :dir => (aWorkingDir || orig_wd)}
57
- status = nil
58
- begin
59
- Dir.chdir(aWorkingDir) if aWorkingDir
60
- Timeout.timeout(aTimeout,ExecuteError) do # nil aTimeout will not time out
61
- status = POpen4::popen4(aCommand) do |stdout, stderr, stdin, pid|
62
- result[:stdout] = stdout.read
63
- result[:stderr] = stderr.read
64
- result[:pid] = pid
65
- end
66
- end
67
- ensure
68
- Dir.chdir(orig_wd)
69
- end
70
- result[:exitcode] = (status && status.exitstatus) || 1
71
- yield(result) if block_given?
72
- raise ExecuteError.new(result) if result[:exitcode] != 0
73
- return result
74
- end
75
-
76
- end
77
20
 
78
21
 
79
22
  module YoreCore
@@ -137,88 +80,6 @@ module YoreCore
137
80
  end
138
81
 
139
82
 
140
- class ConfigClass < Hash
141
-
142
- def initialize(aDefaultHash=nil)
143
- self.merge!(aDefaultHash) if aDefaultHash
144
- end
145
-
146
- def set_int(aKey,aValue)
147
- case aValue
148
- when String then self[aKey] = aValue.to_integer(self[aKey]);
149
- when Fixnum then self[aKey] = aValue;
150
- when Float then self[aKey] = aValue.to_i;
151
- end
152
- end
153
-
154
- def set_float(aKey,aValue)
155
- case aValue
156
- when String then self[aKey] = aValue.to_float(self[aKey]);
157
- when Fixnum then self[aKey] = aValue.to_f;
158
- when Float then self[aKey] = aValue;
159
- end
160
- end
161
-
162
- def set_boolean(aKey,aValue)
163
- case aValue
164
- when TrueClass,FalseClass then self[aKey] = aValue;
165
- when String then self[aKey] = (['1','yes','y','true','on'].include?(aValue.downcase))
166
- else
167
- default set_boolean(aKey,aValue.to_s)
168
- end
169
- end
170
-
171
- def set_symbol(aKey,aValue)
172
- case aValue
173
- when String then self[aKey] = (aValue.to_sym rescue nil);
174
- when Symbol then self[aKey] = aValue;
175
- end
176
- end
177
-
178
- def copy_item(aHash,aKey)
179
- case self[aKey]
180
- when NilClass then ;
181
- when String then self[aKey] = aHash[aKey].to_s unless aHash[aKey].nil?
182
- when Float then set_float(aKey,aHash[aKey]);
183
- when Fixnum then set_int(aKey,aHash[aKey]);
184
- when TrueClass, FalseClass then set_boolean(aKey,aHash[aKey]);
185
- when Symbol then self[aKey] = (aHash[aKey].to_sym rescue nil)
186
- else
187
- raise Error.new('unsupported type')
188
- end
189
- end
190
-
191
- def copy_strings(aHash,*aKeys)
192
- aKeys.each do |k|
193
- self[k] = aHash[k].to_s unless aHash[k].nil?
194
- end
195
- end
196
-
197
- def copy_ints(*aDb)
198
- aHash = aDb.shift
199
- aKeys = aDb
200
- aKeys.each do |k|
201
- set_int(k,aHash[k])
202
- end
203
- end
204
-
205
- def copy_floats(aHash,*aKeys)
206
- aKeys.each do |k|
207
- set_float(k,aHash[k])
208
- end
209
- end
210
-
211
- def copy_booleans(aHash,*aKeys)
212
- aKeys.each do |k|
213
- set_boolean(k,aHash[k])
214
- end
215
- end
216
-
217
- def to_hash
218
- {}.merge(self)
219
- end
220
-
221
- end
222
83
 
223
84
  class Yore
224
85
 
@@ -230,7 +91,7 @@ module YoreCore
230
91
  :crypto_key => "07692FC8656F04AE5518B80D38681E038A3C12050DF6CC97CEEC33D800D5E2FE", # apparently a string of up to 64 random hex digits
231
92
  :first_hour => 4,
232
93
  :prefix => 'backup',
233
- :log_level => 'DEBUG',
94
+ :log_level => 'INFO',
234
95
  :bucket => '',
235
96
  :email_report => false,
236
97
  :mail_host => '',
@@ -254,17 +115,26 @@ module YoreCore
254
115
 
255
116
  def initialize(aConfig=nil)
256
117
  DEFAULT_CONFIG[:email_report] = false # fixes some bug where this was nil
257
- configure(aConfig || DEFAULT_CONFIG)
258
- end
118
+ @config = ConfigClass.new(DEFAULT_CONFIG,aConfig)
259
119
 
260
- def self.clean_config(aConfig)
261
- result = ConfigClass.new(DEFAULT_CONFIG)
262
- DEFAULT_CONFIG.each do |k,v|
263
- result.copy_item(aConfig,k) if aConfig[k]
264
- end
265
- #result.copy_ints(aConfig,:keep_daily,:keep_weekly,:keep_monthly)
266
- #result.copy_strings(aConfig,:crypto_iv,:crypto_key,:prefix,:bucket,:log_level,:basepath)
267
- result.to_hash
120
+ cons = ConsoleLogger.new()
121
+ cons.level = Logger::Severity.const_get(config[:log_level]) rescue Logger::Severity::INFO
122
+
123
+ report_file = MiscUtils::temp_file
124
+ @reporter = Logger.new(report_file)
125
+ @reporter.formatter = ConsoleLogger::ReportFormatter.new
126
+ @reporter.level = cons.level
127
+
128
+ @logger = MultiLogger.new([cons,@reporter])
129
+ #require 'ruby-debug'; debugger
130
+ @logger.info "Yore file and database backup tool for Amazon S3 "
131
+ @logger.info "(c) 2009 Buzzware Solutions (www.buzzware.com.au)"
132
+ @logger.info "-------------------------------------------------"
133
+ @logger.info ""
134
+
135
+ @logger.info "report file: #{report_file}"
136
+
137
+ configure(@config)
268
138
  end
269
139
 
270
140
  # read the config however its given and return a hash with values in their correct type, and either valid or nil
@@ -272,45 +142,47 @@ module YoreCore
272
142
  def configure(aConfig,aCmdOptions = nil,aOptions = nil)
273
143
  config_h = nil
274
144
  case aConfig
275
- when Hash then config_h = aConfig
145
+ when Hash,::ConfigClass then config_h = aConfig
276
146
  when REXML::Element then config_h = XmlUtils.read_simple_items(aConfig,'/Yore/SimpleItems')
277
147
  else
278
- raise Error.new('unsupported type')
148
+ raise StandardError.new('unsupported type')
279
149
  end
280
150
  config_i = {}
281
151
  config_h.each{|n,v| config_i[n.to_sym] = v} if config_h
282
152
  aCmdOptions.each{|k,v| config_i[k.to_sym] = v} if aCmdOptions
283
153
  config_i.merge!(aOptions) if aOptions
284
- @config = Yore.clean_config(config_i)
154
+ config.read(config_i)
155
+
285
156
  @keepers = Array.new
286
157
  @keepers << KeepDaily.new(config[:keep_daily])
287
158
  @keepers << KeepWeekly.new(config[:keep_weekly])
288
159
  @keepers << KeepMonthly.new(config[:keep_monthly])
289
-
290
- @log_file = MiscUtils::temp_file()
291
-
292
- @logger = LogUtils::create_logger_from_config({
293
- 'destination' => 'STDOUT',
294
- 'level' => config[:log_level]
295
- # 'destination' => 'FILE',
296
- # 'filename' => @log_file,
297
- # 'level' => config[:log_level]
298
- })
299
- report_file = MiscUtils::temp_file
300
- logger.info "report file: #{report_file}"
301
- @reporter = LogUtils::create_reporter(report_file)
160
+
302
161
  @basepath = config_h[:basepath]
303
- #sources = aConfig
304
- #
305
- #@filelist =
306
162
  end
163
+
164
+ def do_action(aAction,aArgs)
165
+ logger.info "Executing command: #{aAction} ...\n"
166
+ begin
167
+ send(aAction,aArgs)
168
+ rescue Exception => e
169
+ logger.warn "#{e.class.to_s}: during #{aAction}(#{aArgs.inspect}): #{e.message}"
170
+ end
171
+ end
307
172
 
308
173
  def shell(aCommandline,&aBlock)
309
- reporter.debug "To shell: " + aCommandline
174
+ #require 'ruby-debug'; debugger
175
+ logger.debug "To shell: " + aCommandline
310
176
  result = block_given? ? POpen4::shell(aCommandline,nil,nil,&aBlock) : POpen4::shell(aCommandline)
311
- reporter.debug "From shell: '#{result[:stdout]}'"
177
+ logger.debug "From shell: '#{result.inspect}'"
312
178
  return result[:stdout]
313
179
  end
180
+
181
+ def s3shell(aCommandline)
182
+ shell(aCommandline) do |r|
183
+ r[:exitcode] = 1 if r[:stderr].length > 0
184
+ end
185
+ end
314
186
 
315
187
  def get_log
316
188
  logger.close
@@ -318,7 +190,7 @@ module YoreCore
318
190
  end
319
191
 
320
192
  def get_report
321
- MiscUtils::string_from_file(reporter.logdev.filename)
193
+ MiscUtils::string_from_file(@reporter.logdev.filename)
322
194
  end
323
195
 
324
196
  def self.filemap_from_filelist(aFiles)
@@ -334,11 +206,21 @@ module YoreCore
334
206
 
335
207
  end
336
208
 
209
+ #def self.nice_format(aNumber)
210
+ # if aNumber >= 100
211
+ # sprintf('%.0f', aNumber)
212
+ # else
213
+ # sprintf('%.3f', aNumber).sub(/\.0{1,3}$/, '')
214
+ # end
215
+ #end
216
+
337
217
  # By default, GNU tar suppresses a leading slash on absolute pathnames while creating or reading a tar archive. (You can suppress this with the -p option.)
338
218
  # tar : http://my.safaribooksonline.com/0596102461/I_0596102461_CHP_3_SECT_9#snippet
339
219
 
340
220
  # get files from wherever they are into a single file
341
221
  def collect(aSourceFiles,aDestFile,aParentDir=nil)
222
+ logger.info "Collecting files ..."
223
+ logger.info aSourceFiles.join("\n")
342
224
  filelist = filemap = nil
343
225
  if aSourceFiles.is_a?(Hash)
344
226
  filelist = aSourceFiles.keys
@@ -354,12 +236,17 @@ module YoreCore
354
236
  listfile
355
237
  )
356
238
  tarfile = MiscUtils.file_change_ext(aDestFile, 'tar')
239
+
357
240
  shell("tar cv --directory=#{aParentDir} --file=#{tarfile} --files-from=#{listfile}")
358
241
  shell("tar --append --directory=#{aParentDir} --file=#{tarfile} .contents")
242
+ logger.info "Compressing ..."
243
+ tarfile_size = File.size(tarfile)
359
244
  shell("bzip2 #{tarfile}; mv #{tarfile}.bz2 #{aDestFile}")
245
+ logger.info "Compressed #{'%.1f' % (tarfile_size*1.0/2**10)} KB to #{'%.1f' % (File.size(aDestFile)*1.0/2**10)} KB"
360
246
  end
361
247
 
362
248
  def pack(aFileIn,aFileOut)
249
+ logger.info "Encrypting ..."
363
250
  shell "openssl enc -aes-256-cbc -K #{config[:crypto_key]} -iv #{config[:crypto_iv]} -in #{aFileIn} -out #{aFileOut}"
364
251
  end
365
252
 
@@ -369,18 +256,20 @@ module YoreCore
369
256
 
370
257
  def ensure_bucket(aBucket=nil)
371
258
  aBucket ||= config[:bucket]
372
- shell "s3cmd createbucket #{aBucket}"
259
+ logger.info "Ensuring S3 bucket #{aBucket} exists ..."
260
+ s3shell "s3cmd createbucket #{aBucket}"
373
261
  end
374
262
 
375
263
  # uploads the given file to the current bucket as its basename
376
264
  def upload(aFile)
377
265
  ensure_bucket()
378
- shell "s3cmd put #{config[:bucket]}:#{File.basename(aFile)} #{aFile}"
266
+ logger.info "Uploading #{File.basename(aFile)} to S3 bucket #{config[:bucket]} ..."
267
+ s3shell "s3cmd put #{config[:bucket]}:#{File.basename(aFile)} #{aFile}"
379
268
  end
380
269
 
381
270
  # downloads the given file from the current bucket as its basename
382
271
  def download(aFile)
383
- shell "s3cmd get #{config[:bucket]}:#{File.basename(aFile)} #{aFile}"
272
+ s3shell "s3cmd get #{config[:bucket]}:#{File.basename(aFile)} #{aFile}"
384
273
  end
385
274
 
386
275
  # calculate the date (with no time component) based on :day_begins_hour and the local time
@@ -407,7 +296,6 @@ module YoreCore
407
296
  #end
408
297
 
409
298
  def backup_process(aSourceFiles,aTimeNow=Time.now,aTempDir=nil)
410
- #require 'ruby-debug'; debugger
411
299
  aTempDir ||= MiscUtils.make_temp_dir('yore_')
412
300
  temp_file = File.expand_path('backup.tar',aTempDir)
413
301
  collect(aSourceFiles,temp_file)
@@ -418,6 +306,7 @@ module YoreCore
418
306
 
419
307
  # aDb : Hash containing :db_host,db_user,db_password,db_name,
420
308
  def db_to_file(aDb,aFile)
309
+ logger.info "Dumping database #{aDb[:db_name]} ..."
421
310
  shell "#{config[:mysqldump]} --host=#{aDb[:db_host]} --user=#{aDb[:db_user]} --password=#{aDb[:db_password]} --databases --skip-extended-insert --add-drop-database #{aDb[:db_name]} > #{aFile}"
422
311
  end
423
312
 
@@ -449,6 +338,7 @@ module YoreCore
449
338
  def report
450
339
  return unless config[:email_report]
451
340
  msg = get_report()
341
+ logger.info "Sending report via email to #{config[:mail_to]} ..."
452
342
  MiscUtils::send_email(
453
343
  :host => config[:mail_host],
454
344
  :port => config[:mail_port],
@@ -476,7 +366,6 @@ module YoreCore
476
366
  end
477
367
 
478
368
  def backup(aJobFiles)
479
- #require 'ruby-debug'; debugger
480
369
  return unless job = aJobFiles.is_a?(Array) ? aJobFiles.first : aJobFiles # just use first job
481
370
 
482
371
  xmlRoot = XmlUtils.get_file_root(job)
@@ -501,16 +390,16 @@ module YoreCore
501
390
  args = Yore::database_from_xml(xmlDb)
502
391
  file = args.delete(:file)
503
392
  unless args[:db_host] && args[:db_user] && args[:db_password] && args[:db_name] && file
504
- raise Error.new("Invalid or missing parameter")
393
+ raise StandardError.new("Invalid or missing parameter")
505
394
  end
506
395
  db_to_file(args,file)
507
- filelist += file
396
+ filelist << file
508
397
  sourceFound = true
509
398
  end
510
399
  end
511
400
  end
512
401
 
513
- raise Error.new("Backup source found but file list empty") if sourceFound && filelist.empty?
402
+ raise StandardError.new("Backup source found but file list empty") if sourceFound && filelist.empty?
514
403
 
515
404
  filelist.uniq!
516
405
  filelist.sort!
@@ -520,7 +409,6 @@ module YoreCore
520
409
 
521
410
  backup_process(filelist,time,tempdir)
522
411
  #clean
523
- report
524
412
  end
525
413
 
526
414
  def test_email(*aDb)
@@ -553,11 +441,11 @@ module YoreCore
553
441
  else
554
442
  xmlDb = XmlUtils::single_node(xmlRoot,"/Yore/Sources/Source[@Type='MySql']/Database")
555
443
  end
556
- raise Error.new("No database") unless xmlDb
444
+ raise StandardError.new("No database") unless xmlDb
557
445
  args = Yore.database_from_xml(xmlDb)
558
446
  file = args.delete(:file)
559
447
  unless args[:db_host] && args[:db_user] && args[:db_password] && args[:db_name] && file
560
- raise Error.new("Invalid or missing parameter")
448
+ raise StandardError.new("Invalid or missing parameter")
561
449
  end
562
450
  db_to_file(args,file)
563
451
  end
data/test/test_job_a.xml CHANGED
@@ -8,7 +8,7 @@
8
8
  <Item Name="crypto_key">07692FC8656F04AE5518B80D38681E038A3C12050DF6CC97CEEC33D800D5E2FE</Item>
9
9
  <Item Name="first_hour">4</Item>
10
10
  <Item Name="prefix">backup</Item>
11
- <Item Name="log_level">DEBUG</Item>
11
+ <Item Name="log_level">INFO</Item>
12
12
  </SimpleItems>
13
13
  <Sources>
14
14
  <Source>
data/test/test_job_b.xml CHANGED
@@ -9,7 +9,7 @@
9
9
  <Item Name="bucket">yoretest</Item>
10
10
  <Item Name="first_hour">4</Item>
11
11
  <Item Name="prefix">yoretest</Item>
12
- <Item Name="log_level">DEBUG</Item>
12
+ <Item Name="log_level">INFO</Item>
13
13
  <Item Name="email_report">false</Item>
14
14
 
15
15
  <Item Name="mail_host">mail.authsmtp.com</Item>
data/test/yore_test.rb CHANGED
@@ -7,6 +7,7 @@ gem 'Shoulda'; require 'shoulda'
7
7
 
8
8
 
9
9
  require 'ihl_ruby/misc_utils'
10
+ require 'ihl_ruby/shell_extras'
10
11
 
11
12
  require 'fileutils'
12
13
 
@@ -126,7 +127,7 @@ job_template = <<EOF
126
127
  <Item Name="crypto_iv">3A63775C1E3F291B0925578165EB917E</Item>
127
128
  <Item Name="crypto_key">07692FC8656F04AE5518B80D38681E038A3C12050DF6CC97CEEC33D800D5E2FE</Item>
128
129
  <Item Name="prefix">backup</Item>
129
- <Item Name="log_level">DEBUG</Item>
130
+ <Item Name="log_level">INFO</Item>
130
131
  <Item Name="bucket">${BUCKET}</Item>
131
132
  </SimpleItems>
132
133
  <Sources>
@@ -186,6 +187,14 @@ EOF
186
187
  aController = YoreCore::Yore.new # main program object
187
188
  job = File.expand_path('../../test/test_job_b.xml',THIS_DIR)
188
189
  xmlRoot = XmlUtils.get_file_root(job)
190
+ srcdir = '/tmp/yoretest'
191
+ FileUtils.rm_rf srcdir+'/*'
192
+ FileUtils.mkdir_p srcdir
193
+ ['a','a/1','b','c'].each {|p| FileUtils.mkdir_p(File.expand_path(p,srcdir))}
194
+ %w(a/blah.txt a/1/blahblah.txt b/apples.txt c/carrots.txt).each do |f|
195
+ MiscUtils::make_temp_file(f,srcdir)
196
+ end
197
+
189
198
  aController.configure(xmlRoot,nil,{:basepath => File.dirname(File.expand_path(job))})
190
199
  aController.backup(job)
191
200
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gary McGhee
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-04 00:00:00 +09:00
12
+ date: 2009-02-20 00:00:00 +09:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -90,8 +90,11 @@ files:
90
90
  - lib/ihl_ruby/enum.rb
91
91
  - lib/ihl_ruby/extend_base_classes.rb
92
92
  - lib/ihl_ruby/misc_utils.rb
93
+ - lib/ihl_ruby/config.rb
94
+ - lib/ihl_ruby/logging.rb
93
95
  - lib/ihl_ruby/string_utils.rb
94
96
  - lib/ihl_ruby/xml_utils.rb
97
+ - lib/ihl_ruby/shell_extras.rb
95
98
  - lib/yore.orig.rb
96
99
  - lib/yore/yore_core.rb
97
100
  - Manifest.txt