remotebackup 0.5.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.
@@ -0,0 +1,279 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+ require 'rubygems'
4
+ require 'net/ssh'
5
+ require 'net/scp'
6
+ require "stringio"
7
+ require "fileutils"
8
+ require "rexml/document"
9
+ require "date"
10
+ require 'logger'
11
+ require 'yaml'
12
+ module Remotebackup
13
+ class Node
14
+ attr_accessor :ftype,:name,:makeMap
15
+ Result = {:access=>0,:link_num=>1,:user=>2,:group=>3,:size=>4,:month=>5,:day=>6,:yt=>7,:name=>8,:arrow=>9,:source=>10}
16
+ Month = {"Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8,
17
+ "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12}
18
+ def initialize(out)
19
+ result = out.split();
20
+ @name = result[Result[:name]]
21
+ case result[Result[:access]]
22
+ when /^-/
23
+ @ftype = :file
24
+ @size = result[Result[:size]].to_i
25
+ year = DateTime.now.year
26
+ month = Month[result[Result[:month]]]
27
+ day = result[Result[:day]].to_i
28
+ hour = 0
29
+ minute = 0
30
+ if result[Result[:yt]] =~ /(.*):(.*)/
31
+ hour = $1.to_i
32
+ minute = $2.to_i
33
+ else
34
+ year = result[Result[:yt]].to_i
35
+ end
36
+ @date = DateTime.new(year,month,day,hour,minute).to_s
37
+ @makeMap = lambda{ return {"size" => @size, "date" => @date}}
38
+ when /^l/
39
+ @ftype = :symbolic
40
+ @source = result[Result[:source]]
41
+ @makeMap = lambda{ return {"source" => @source}}
42
+ when /^d/
43
+ @ftype = :directory
44
+ else
45
+ @ftype = :special
46
+ $log.error("#{@name} is not regular file. ignore.")
47
+ end
48
+ end
49
+ end
50
+ class BackupInfo
51
+ attr_accessor :fileMap,:mod,:last_file
52
+ def initialize(name,server,user,password,path,ignore_list)
53
+ @mod = false
54
+ @name = name
55
+ @server = server
56
+ @user = user
57
+ @password = password
58
+ @path = path
59
+ @ignore_list = ignore_list
60
+ @nowTime = DateTime.now
61
+ @fileMap = {"file" => Hash.new,"symbolic" => Hash.new,"directory"=>Array.new}
62
+ Net::SSH.start(@server,@user,:password=>@password) { |session|
63
+ makeFileMapList(session,"")
64
+ }
65
+ end
66
+ def ignore_check(target)
67
+ if @ignore_list
68
+ @ignore_list.each do |ignore|
69
+ if target =~ /#{ignore}/
70
+ return false
71
+ end
72
+ end
73
+ end
74
+ return true
75
+ end
76
+ def makeFileMapList(session,target)
77
+ if target != ""
78
+ target_path = @path +"/"+target
79
+ else
80
+ target_path = @path
81
+ end
82
+ cmd = "export LANG=C;ls -la #{target_path}"
83
+ out = ""
84
+ channel = session.open_channel do |ch|
85
+ ch.exec cmd do |ch,success|
86
+ ch.on_data do |c,data|
87
+ out = data
88
+ end
89
+ end
90
+ end
91
+ channel.wait
92
+ if out =~ /No such file/
93
+ $log.error("can't open path:#{target_path}")
94
+ return
95
+ end
96
+ if out == ""
97
+ return
98
+ end
99
+ lines = StringIO.new(out).readlines
100
+ if lines[0].split().length < 9
101
+ @fileMap["directory"]. push target unless target == ""
102
+ makeFileMap(session,target,lines)
103
+ else
104
+ node = Node.new(lines[0].chomp)
105
+ if node.ftype == :symbolic
106
+ @fileMap["symbolic"][File.basename(@path)] = node.makeMap.call
107
+ elsif node.ftype == :file
108
+ @fileMap["file"][File.basename(@path)] = node.makeMap.call
109
+ end
110
+ end
111
+ end
112
+ def makeFileMap(session,target,lines)
113
+ lines.each do |line|
114
+ if line.split().length < 9
115
+ next
116
+ end
117
+ node = Node.new(line.chomp)
118
+ if node.name == "." or node.name == ".."
119
+ next
120
+ end
121
+ if target != ""
122
+ target_name = target +"/"+ node.name
123
+ else
124
+ target_name = node.name
125
+ end
126
+ unless ignore_check(target_name)
127
+ next
128
+ end
129
+ case node.ftype
130
+ when :directory
131
+ makeFileMapList(session,target_name)
132
+ when :special
133
+ next
134
+ when :file
135
+ @fileMap["file"][target_name] = node.makeMap.call
136
+ when :symbolic
137
+ @fileMap["symbolic"][target_name] = node.makeMap.call
138
+ end
139
+ end
140
+ end
141
+ def write_to_yaml(out_dir)
142
+ output_dir = out_dir + "/" + @name
143
+ FileUtils.mkdir_p(output_dir)
144
+ file_name = output_dir + "/" + date_to_filename + ".yml"
145
+ File.open(file_name ,"w") do |f|
146
+ f.write YAML.dump(@fileMap)
147
+ end
148
+ end
149
+ def load_last_yaml(output_dir)
150
+ files = Array.new
151
+ if FileTest.directory?(output_dir)
152
+ dirs = Dir.glob(output_dir+ "/*.yml")
153
+ dirs.each do |file|
154
+ files.push file
155
+ end
156
+ end
157
+ if files.length == 0
158
+ return {"file" => Hash.new,"symbolic" => Hash.new,"directory"=>Array.new}
159
+ end
160
+ @last_file = files.sort.pop
161
+ return YAML.load(File.read(@last_file))
162
+ end
163
+ def differencial_copy(out_dir)
164
+ output_dir = out_dir + "/" + @name
165
+ oldFileInfoMap = load_last_yaml(output_dir)
166
+ @fileMap["directory"].each do |dir|
167
+ unless oldFileInfoMap["directory"].include?(dir)
168
+ @mod = true
169
+ msg_out "create directory:#{dir}"
170
+ FileUtils.mkdir_p(output_dir + "/" + dir)
171
+ end
172
+ end
173
+ oldFileMap = oldFileInfoMap["symbolic"]
174
+ @fileMap["symbolic"].each do |key,val|
175
+ if !oldFileMap[key] || oldFileMap[key]["source"] != val["source"]
176
+ @mod = true
177
+ if !oldFileMap[key]
178
+ msg_out "create symbolic:#{key}"
179
+ else
180
+ msg_out "modified symbolic:#{key}"
181
+ end
182
+ end
183
+ end
184
+ oldFileMap = oldFileInfoMap["file"]
185
+ @fileMap["file"].each do |key,val|
186
+ if !oldFileMap[key] || oldFileMap[key]["size"] != val["size"] || oldFileMap[key]["date"] != val["date"]
187
+ @mod = true
188
+ file_name = output_dir + "/" + key + date_to_filename
189
+ msg_out "scp #{@user}@#{@server}:#{@path}/#{key} #{file_name} #password=#{@password}"
190
+ Net::SSH.start(@server, @user, :password => @password) do |ssh|
191
+ ssh.scp.download! "#{@path}/#{key}",file_name
192
+ end
193
+ val["file_name"] = file_name
194
+ if !oldFileMap[key]
195
+ msg_out "create file:#{key}"
196
+ else
197
+ msg_out "modified file:#{key}"
198
+ end
199
+ else
200
+ val["file_name"] = oldFileMap[key]["file_name"]
201
+ end
202
+ end
203
+ end
204
+ def zerosup(i)
205
+ if i < 10
206
+ "0" + i.to_s
207
+ else
208
+ i.to_s
209
+ end
210
+ end
211
+ def date_to_filename()
212
+ @nowTime.year.to_s + "_" + zerosup(@nowTime.month) + "_" + zerosup(@nowTime.day) + "_" + zerosup(@nowTime.hour) + "_" + zerosup(@nowTime.min)
213
+ end
214
+ end
215
+ class Backup
216
+ def initialize(config_file,config_out_dir)
217
+ @config_file = config_file
218
+ @config_out_dir = config_out_dir
219
+ @doc = REXML::Document.new(File.open(@config_file))
220
+ @top = @doc.elements["/backups"]
221
+ @conf_backups = Array.new
222
+ unless @top
223
+ oops("/backups is not set in #{@config_file}.")
224
+ end
225
+ @top.each_element do |backup|
226
+ bkup_info = Hash.new
227
+ backup.each_element do |elem|
228
+ if elem.name == "ignore_list"
229
+ ignores = Array.new
230
+ elem.each_element do |ignore|
231
+ ignores.push(ignore.text)
232
+ end
233
+ bkup_info["ignore_list"] = ignores
234
+ else
235
+ bkup_info[elem.name] = elem.text
236
+ end
237
+ end
238
+ @conf_backups.push(bkup_info)
239
+ end
240
+ end
241
+ def start
242
+ i=1
243
+ @conf_backups.each do |conf|
244
+ bkup = BackupInfo.new(conf["name"],conf["server"],conf["user"],conf["password"],conf["path"],conf["ignore_list"])
245
+ msg_out "--------------------------------"
246
+ msg_out "Backup start #{conf['name']}"
247
+ msg_out "--------------------------------"
248
+ bkup.differencial_copy(@config_out_dir)
249
+ if bkup.mod
250
+ bkup.write_to_yaml(@config_out_dir)
251
+ else
252
+ FileUtils.touch([bkup.last_file])
253
+ end
254
+ end
255
+ end
256
+ end
257
+ class Restore
258
+ def initialize(config_file,config_out_dir)
259
+ @config_file = config_file
260
+ @file_info_map = YAML.load_file(config_file)
261
+ @config_out_dir = config_out_dir
262
+ FileUtils.mkdir_p(@config_out_dir)
263
+ end
264
+ def start
265
+ msg_out "-------------------------------------------"
266
+ msg_out "Restore start #{File.dirname(@config_file)}"
267
+ msg_out "-------------------------------------------"
268
+ @file_info_map["directory"].each do |dir|
269
+ FileUtils.mkdir_p(@config_out_dir + "/" + dir)
270
+ end
271
+ @file_info_map["symbolic"].each do |key,val|
272
+ FileUtils.symlink(val["source"],@config_out_dir + "/" + key)
273
+ end
274
+ @file_info_map["file"].each do |key,val|
275
+ FileUtils.cp(val["file_name"],@config_out_dir + "/" + key)
276
+ end
277
+ end
278
+ end
279
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/remotebackup.rb'}"
9
+ puts "Loading remotebackup gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/script/txt2html ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ GEM_NAME = 'remotebackup' # what ppl will type to install your gem
4
+ RUBYFORGE_PROJECT = 'remotebackup'
5
+
6
+ require 'rubygems'
7
+ begin
8
+ require 'newgem'
9
+ require 'rubyforge'
10
+ rescue LoadError
11
+ puts "\n\nGenerating the website requires the newgem RubyGem"
12
+ puts "Install: gem install newgem\n\n"
13
+ exit(1)
14
+ end
15
+ require 'redcloth'
16
+ require 'syntax/convertors/html'
17
+ require 'erb'
18
+ require File.dirname(__FILE__) + "/../lib/#{GEM_NAME}/version.rb"
19
+
20
+ version = Remotebackup::VERSION::STRING
21
+ download = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
22
+
23
+ def rubyforge_project_id
24
+ RubyForge.new.autoconfig["group_ids"][RUBYFORGE_PROJECT]
25
+ end
26
+
27
+ class Fixnum
28
+ def ordinal
29
+ # teens
30
+ return 'th' if (10..19).include?(self % 100)
31
+ # others
32
+ case self % 10
33
+ when 1: return 'st'
34
+ when 2: return 'nd'
35
+ when 3: return 'rd'
36
+ else return 'th'
37
+ end
38
+ end
39
+ end
40
+
41
+ class Time
42
+ def pretty
43
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
44
+ end
45
+ end
46
+
47
+ def convert_syntax(syntax, source)
48
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
49
+ end
50
+
51
+ if ARGV.length >= 1
52
+ src, template = ARGV
53
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.html.erb')
54
+ else
55
+ puts("Usage: #{File.split($0).last} source.txt [template.html.erb] > output.html")
56
+ exit!
57
+ end
58
+
59
+ template = ERB.new(File.open(template).read)
60
+
61
+ title = nil
62
+ body = nil
63
+ File.open(src) do |fsrc|
64
+ title_text = fsrc.readline
65
+ body_text_template = fsrc.read
66
+ body_text = ERB.new(body_text_template).result(binding)
67
+ syntax_items = []
68
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
69
+ ident = syntax_items.length
70
+ element, syntax, source = $1, $2, $3
71
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
72
+ "syntax-temp-#{ident}"
73
+ }
74
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
75
+ body = RedCloth.new(body_text).to_html
76
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
77
+ end
78
+ stat = File.stat(src)
79
+ created = stat.ctime
80
+ modified = stat.mtime
81
+
82
+ $stdout << template.result(binding)