Class RsyncBackup
In: rsync_backup.rb
Parent: Object

RsyncBackup

 Rsync によるバックアップを行うためのクラス。

Methods

Attributes

dstdir  [R]  転送先のディレクトリ. RsyncBackup.new で指定 (必須)
dsthost  [R]  転送先のホスト. RsyncBackup.new で指定 (指定しない場合, ローカルとなる)
exclude_list  [R]  rsync の —exclude オプションの対象となるリスト. RsyncBackup.excludeopt で指定できる.
logfile_output  [R] 
noexec  [R] 
rsync_opt_check  [R]  rsync の "-n" オプションをつけるためのフラグ. RsyncBackup.checkopt メソッドで指定できる.
rsync_opt_delete  [R] 
rsync_opt_rsh  [R] 
src_dst_hash  [R] 
src_exclude_hash  [R] 
srcdir  [R]  転送元のディレクトリ. RsyncBackup.new で指定 (必須)
srchost  [R]  転送元のホスト. RsyncBackup.new で指定 (指定しない場合, ローカルとなる)
user  [R]  ユーザ名. デフォルトは nil で, その場合には rsync コマンドに ユーザを指定しない.

Public Class methods

srcdir と srchost に転送元のディレクトリとホストを指定する。 dstdir と dsthost に転送先のディレクトリとホストを指定する。 srchost, dsthost は、指定されない場合はローカルだと仮定する。 srcdir と dstdir を指定しない場合にはエラーを発生させる。

[Source]

     # File rsync_backup.rb, line 139
139:   def initialize(srcdir=nil, srchost=nil, dstdir=nil, dsthost=nil)
140:     @srcdir   = srcdir
141:     @srchost  = srchost
142:     @dstdir   = dstdir
143:     @dsthost  = dsthost
144: 
145:     # @srcdir, @dstdir が文字変数でない or 空白の場合はエラー
146:     if !str_and_notspace?(@srcdir) then
147:       raise ArgumentError, "\"srcdir\" is not specified.\n"
148:     end
149:     if !str_and_notspace?(@dstdir) then
150:       raise ArgumentError, "\"dstdir\" is not specified.\n"
151:     end
152: 
153:     # @srchost と @dsthost の両方が有効な場合にはエラー
154:     if str_and_notspace?(@srchost) && str_and_notspace?(@dsthost) then
155:       raise ArgumentError, "rsync is not supported to transfer from remote src to remote destination.\n"
156:     end
157: 
158:     debug(@srcdir, @srchost, @dstdir, @dsthost)
159: 
160:     self.noexec(nil)
161:     self.logfile(nil, true)
162:     self.rsh
163:     self.checkopt(nil)
164:     self.deleteopt(nil)
165:     self.excludeopt(nil, true)
166:     self.username(nil)
167:     self.partly
168:   end

Public Instance methods

rsync に "-n" オプションをつけて動作させるためのメソッド。 これにより、転送するファイルやディレクトリの一覧を 表示するだけで、実際にはデータの転送を行わないようにする。

check に nil を与えると "-n" オプションをはずす。

[Source]

     # File rsync_backup.rb, line 247
247:   def checkopt(check=true)
248:     @rsync_opt_check = (check) ? "-n" : " "
249: 
250:     debug(@rsync_opt_check)
251:   end

rsync に "—delete" オプションをつけて動作させるためのメソッド。 このオプションにより、転送元に無くて転送先に存在するファイルや ディレクトリを消去するように動作する。

delete に nil を与えると "—delete" オプションをはずす。

[Source]

     # File rsync_backup.rb, line 261
261:   def deleteopt(delete=true)
262:     @rsync_opt_delete = (delete) ? "--delete" : " "
263: 
264:     debug(@rsync_opt_delete)
265:   end

rsync に "—exclude" オプションをつけて動作させるためのメソッド。 このオプションにより、一部のファイルを転送対象からはずすことができる。 複数回呼ぶことが可能である。第1引数には転送対象からはずしたい ファイル名を書く。第2引数に true を与えると今まで与えたファイル名 がリストからクリアされる。

ただし、有効にするには、このメソッドを呼んだ後、必ず RsyncBackup.partly メソッドを呼ぶ必要がある (引数は nil でも良い) ので注意すること。

[Source]

     # File rsync_backup.rb, line 282
282:   def excludeopt(exclude=false, clear=false)
283:     if clear then
284:       @exclude_list = Array.new
285:     end
286: 
287:     if str_and_notspace?(exclude) then
288:       @exclude_list.push(exclude)
289:     end
290: 
291:     debug(@exclude_list)
292:   end

ログをファイルに書き出すためのメソッド、引数 logfile に nil を渡すと ログは標準出力に書き出される。文字列を与えると、そのファイルに対して ログを書き出すようになる。文字列ではなく、且つ true を渡すと、 自動的に /tmp/以下に実行ファイルとプロセス ID を組み合わせた ファイルに書き出す。

デフォルトではログは以前のファイルを上書きするが、overwrite に nil を 与えると、以前のものに追記するようになる。

[Source]

     # File rsync_backup.rb, line 201
201:   def logfile(logfile=true, overwrite=true)
202:     redirect = (overwrite) ? ">" : ">>"
203: 
204:     if logfile.instance_of?(String) && /\w+/ =~ logfile.chomp then
205:       @logfile_output = redirect.strip + " " + logfile.chomp.strip
206:     elsif logfile then
207:       @logfile_output =  redirect.strip + " "
208:       @logfile_output << "/tmp/" + File.basename($0.to_s) + "-" + $$.to_s
209:     else
210:       @logfile_output = " "
211:     end
212: 
213:     debug(@logfile_output)
214:   end

コマンドとして渡す文字列を出力するに留め、実際には rsync を動作させなく するためのメソッド。引数に nil を渡せば、rsync を動作するようにできる。

[Source]

     # File rsync_backup.rb, line 184
184:   def noexec(noexec=true)
185:     @noexec = noexec
186: 
187:     debug(@noexec)
188:   end

一部だけを転送するためのメソッド。

dirs にディレクトリ名を渡すと、RsyncBackup.new で srcdir に指定した ディレクトリをそのまま転送するのではなく、その一部分のみを 転送するようにする。重複するものは無視される。 srcdir 以下のファイルやディレクトリの数が多くなってきた場合に、 dirs にディレクトリを指定して個別にバックアップを行うことを想定 している。 現在の仕様では、複数回 partly を用いた場合、最後の一回のみが 有効になるようになっている。

[Source]

     # File rsync_backup.rb, line 325
325:   def partly(*dirs)
326:     dirs.flatten!  # 配列の平滑化 (1次元配列化)
327:     dirs.delete_if{|dir| !dir.instance_of?(String)} # 文字列でないものは削除
328:     dirs.collect!{|dir| dir = dir.strip} # 前後の空白を除く
329:     dirs.collect!{|dir| dir = File.expand_path(dir)} # フルパスに変更
330:     dirs.uniq!                     # 重複を無くす
331: 
332:     @src_dst_hash     = Hash.new
333:     @src_exclude_hash = Hash.new
334:     if (dirs.size < 1) then
335:       src = merge_dir_host_user(@srcdir, @srchost, @user)
336:       dst = merge_dir_host_user(@dstdir, @dsthost, @user)
337:       @src_dst_hash.store(src, dst)
338: 
339:       base_dir = (!str_and_notspace?(@srchost)) ? @srcdir : @dstdir
340:       rsync_opt_exclude = gen_rsync_opt_exclude(base_dir, @exclude_list)
341:       @src_exclude_hash.store(src, rsync_opt_exclude)
342: 
343:       debug(@src_dst_hash)
344:       debug(@src_exclude_hash)
345:     else
346:       dirs.each{|directory|
347:         debug(directory)
348:         # directory が @srcdir と同じであれば上記設定をして next
349:         if File.expand_path(@srcdir) == File.expand_path(directory) then
350:           src = merge_dir_host_user(@srcdir, @srchost, @user)
351:           dst = merge_dir_host_user(@dstdir, @dsthost, @user)
352:           @src_dst_hash.store(src, dst)
353: 
354:           base_dir = (!str_and_notspace?(@srchost)) ? @srcdir : @dstdir
355:           rsync_opt_exclude = gen_rsync_opt_exclude(base_dir, @exclude_list)
356:           @src_exclude_hash.store(src, rsync_opt_exclude)
357: 
358:           debug(@src_dst_hash)
359:           debug(@src_exclude_hash)
360:           next
361:         end
362: 
363:         # directory が @srcdir を異なる際の動作
364:         splited = split_base_dir(@srcdir, directory)
365:         unless splited then
366:           print "\"#{directory}\" is not included \"#{@srcdir}\".\n"
367:           next
368:         end
369: 
370:         src = merge_dir_host_user(File.join(@srcdir, splited), @srchost, @user)
371:         dst = merge_dir_host_user(File.join(@dstdir, splited), @dsthost, @user)
372: 
373:         @src_dst_hash.store(src, dst)
374: 
375:         rsync_opt_exclude = gen_rsync_opt_exclude(directory, @exclude_list)
376:         @src_exclude_hash.store(src, rsync_opt_exclude)
377:       }
378:       debug(@src_dst_hash)
379:       debug(@src_exclude_hash)
380:     end
381:   end

rsync の転送方法を指定するためのメソッド。何も指定しない場合は "-e ssh" が用いられる。もしも RsyncBackup.new にて設定した @srchost と @dsthost が両方とも無効 (文字列で無い、または 空文字) である場合には効果を発揮しない。

[Source]

     # File rsync_backup.rb, line 223
223:   def rsh(rsync_rsh=nil)
224:     if rsync_rsh then
225:       @rsync_opt_rsh = rsync_rsh
226:     elsif !@srchost && !@dsthost
227:       @rsync_opt_rsh = " "
228:     else
229:       @rsync_opt_rsh = "-e ssh"
230:     end
231: 
232:     debug(@rsync_opt_rsh)
233:   end

@src_dst_hash に対して rsync コマンドを実行するメソッド。 最終的に呼ばれるべきメソッドである。

[Source]

     # File rsync_backup.rb, line 387
387:   def run(cmd=nil)
388:     cmdstr_base = (cmd) ? cmd : "/usr/bin/env rsync "
389:     cmdstr_base << "#{@rsync_opt_check} "
390:     cmdstr_base << "#{@rsync_opt_delete} "
391:     cmdstr_base << "-av "
392:     cmdstr_base << "#{@rsync_opt_rsh} "
393: 
394:     @src_dst_hash.each{|src, dst|
395:       cmdstr =  ""              # for ruby1.6
396: #      cmdstr =  String.new     # for ruby1.8
397:       cmdstr << cmdstr_base
398:       cmdstr << " #{@src_exclude_hash[src]}"
399:       cmdstr << " #{src} #{dst}"
400:       cmdstr << " #{logfile_output}"
401: 
402:       debug(cmdstr)
403: 
404:       print "#{cmdstr}\n"
405:       system "#{cmdstr}" unless @noexec
406:     }
407:   end

rsync にユーザ名を指定するためのメソッド. user に文字型で且つ空白 のみでない値を与えた場合のみ, 有効になる.

[Source]

     # File rsync_backup.rb, line 303
303:   def username(user=false)
304:     if str_and_notspace?(user) then
305:       @user = user.chomp.strip
306:     else
307:       @user = nil
308:     end
309:     debug(@user)
310:   end

Private Instance methods

代入された変数が、配列で、且つゼロ配列ではないことを 調べるメソッド

[Source]

     # File rsync_backup.rb, line 545
545:   def array_and_notzero?(obj)
546:     debug(obj)
547: 
548:     if obj.instance_of?(Array) && obj.size > 0 then
549:       return true
550:     else
551:       return false
552:     end
553: 
554:   end

デバッグ出力用メソッド。組み込み関数 $DEBUG が真の場合 (つまり、 プログラムを $ ruby -d ./xxxxxx.rb として呼び出した場合) に debug メソッドに代入された変数を出力する。

[Source]

     # File rsync_backup.rb, line 174
174:   def debug(*args)
175:     p [caller.first, *args] if $DEBUG
176:   end

2つ目の引数 (Array オブジェクト) の各パスを1つ目の引数 (String オブジェクト) に合わせた文字列に整形し、最終的に rsync のオプションとして渡す文字列を整形する。

[Source]

     # File rsync_backup.rb, line 490
490:   def gen_rsync_opt_exclude(srcdir=nil, exclude_list=nil)
491:     debug("srcdir=#{srcdir}, exclude_list=", exclude_list)
492: 
493:     rsync_opt_exclude = ""
494: 
495:     # exclude_list が配列で無かったり、サイズが 0 だったら空白文字を返す
496:     if !array_and_notzero?(exclude_list) then
497:       return rsync_opt_exclude
498:     end
499: 
500:     if !str_and_notspace?(srcdir) then
501:       # srcdir が無効な場合の処理
502:       exclude_list.each{|exclude|
503:         rsync_opt_exclude << " --exclude #{exclude}"
504:       }
505:     else
506:       # srcdir が有効な場合
507:       exclude_list.each{|exclude|
508:         formed_exclude_path = Pathname.new(File.expand_path(exclude))
509:         expand_srcdir = Pathname.new(File.expand_path(srcdir))
510: 
511:         formed_exclude =
512:               formed_exclude_path.relative_path_from(expand_srcdir).to_str
513: 
514:         rsync_opt_exclude << " --exclude #{formed_exclude}"
515:       }
516:     end
517: 
518:     debug(rsync_opt_exclude)
519:     return rsync_opt_exclude
520: 
521:   end

ディレクトリ名 dir, ホスト名 host, ユーザ名 user を "user@host:dir/" という rsync で用いる形式にマージする。 ディレクトリ名が無ければ nil を返すが、host や user が 無い場合にはそれぞれの部分が無いパスを返す。 なお、ディレクトリの最後にスラッシュ "/" を付け加えていることに注意 されたし。これは、rsync の src に指定する際に、スラッシュの有無が 動作を大きく変えるためである。詳しくは rsync(1) を参照せよ。

ファイル名やディレクトリ名に空白が入っている場合、 そのディレクトリ名を rsync が正しく解釈できるよう整形する。

[Source]

     # File rsync_backup.rb, line 421
421:   def merge_dir_host_user(dir=nil, host=nil, user=nil)
422:     debug(dir, host, user)
423:     return nil unless dir.instance_of?(String)
424: 
425:     unless host.instance_of?(String) then
426:       if /\w+\s+\w+/ =~ dir.strip then
427:         formed_dir = "\"" + File.expand_path(dir) + "/\""
428:       else
429:         formed_dir = dir.strip + "/"
430:       end
431: 
432:       debug(formed_dir)
433:       return formed_dir
434:     end
435: 
436:     if host.instance_of?(String) then
437:       if /\w+\s+\w+/ =~ dir.strip then
438:         formed_dir = "\"" + dir.strip.gsub(/\s/, '\ ') + "/\""
439:       else
440:         formed_dir = dir.strip + "/"
441:       end
442:     end
443: 
444:     url = host.strip + ":" + formed_dir
445:     debug(url)
446:     return url unless user
447:     url = user.strip + "@" + url
448:     debug(url)
449:     return url
450:   end

dir から base 部分を除いた部分を返す。dir がカレントパスで 記述されていたり、"~" 記法を用いていても自動的に展開する。 もしも dir が base 以下に存在しないディレクトリの場合は nil を返す。

[Source]

     # File rsync_backup.rb, line 458
458:   def split_base_dir(base=nil, dir=nil)
459:     debug("base=#{base}, dir=#{dir}", dir)
460: 
461:     return nil unless dir
462:     fulldir  = File.expand_path(dir)
463:     debug("fulldir=#{fulldir}")
464: 
465:     return fulldir unless base
466:     fullbase = File.expand_path(base)
467:     debug("fullbase=#{fullbase}")
468: 
469:     if /^#{fullbase}\/(.*)$/ =~ fulldir
470:       splited = $1
471:       debug("splited=#{splited}")
472:       return splited
473: 
474:     elsif /^#{fullbase}$/ =~ fulldir
475:       debug("splited=")
476:       return ""
477: 
478:     else
479:       debug("splited=", nil)
480:       return nil
481:     end
482:   end

代入された変数が、文字列で、且つ空白文字のみではないことを 調べるメソッド

[Source]

     # File rsync_backup.rb, line 529
529:   def str_and_notspace?(obj)
530:     debug(obj)
531: 
532:     if obj.instance_of?(String) && /\w+/ =~ obj.chomp.strip then
533:       return true
534:     else
535:       return false
536:     end
537:   end

[Validate]