]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - bin/rbotdb
[registry] standalone import/export/migrate script
[user/henk/code/ruby/rbot.git] / bin / rbotdb
1 #!/usr/bin/env ruby
2 #-- vim:sw=2:et
3 #++
4 #
5 # :title: Registry import/export and migration.
6 #
7 # You can use this script to,
8 #   - backup the rbot registry in a format that is platform independent
9 #   - restore backups
10 #   - migrate old rbot registries bdb (ruby 1.8) and tokyocabinet.
11 #
12 # Author:: apoc (Matthias Hecker) <apoc@geekosphere.org>
13 # Copyright:: (C) 2014 Matthias Hecker
14 # License:: GPLv3
15
16 begin; require 'rubygems'; rescue Exception; puts "[#{$!}]"; end
17 begin; require 'dbm'; rescue Exception; puts "[#{$!}]"; end
18 begin; require 'bdb'; rescue Exception; puts "[#{$!}]"; end
19 begin; require 'tokyocabinet'; rescue Exception; puts "[#{$!}]"; end
20
21 puts 'RBot registry backup/import script.'
22 puts 'Ruby: %s | DBM: %s | BDB: %s | TC: %s' % [RUBY_VERSION, 
23                            (DBM::VERSION rescue '-'), 
24                            (BDB::VERSION rescue '-'),
25                            (TokyoCabinet::VERSION rescue '-')]
26
27 if ARGV.length > 3 or ARGV.length < 2
28   puts """
29   Usage rbotdb [backup|restore] <BACKUP/RESTORE-FILE> [<PROFILE>] 
30
31   Examples:
32     rbotdb backup ~/rbot_db_backup.yaml
33     rbotdb backup ~/rbot_db_backup.yaml.gz ~/.rbot_two
34     rbotdb restore ~/rbot_db_backup.yaml
35   """
36   exit
37 end
38
39 mode = ARGV[0] if %w{backup restore}.include? ARGV[0]
40 file = File.expand_path ARGV[1]
41 profile = File.expand_path(ARGV[2] ? ARGV[2] : '~/.rbot')
42 $last_error = ''
43
44 class Backup
45   class RegistryFile
46     def initialize(registry, path)
47       @registry = registry
48       @path = path
49     end
50     def path
51       @path
52     end
53     def abs
54       File.expand_path(File.join(@registry, @path))
55     end
56     def ext
57       File.extname(@path)
58     end
59     def valid?
60       File.file?(abs) and %w{.db .tdb}.include? ext
61     end
62   end
63
64   def initialize(profile)
65     @profile = profile
66     @registry = File.join(profile, './registry')
67   end
68
69   # list all database files:
70   def list
71     return nil if not File.directory? @registry
72     Dir.chdir @registry
73     Dir.glob(File.join('**', '*')).map do |name|
74       RegistryFile.new(@registry, name)
75     end
76   end
77
78   def load(ext='.db')
79     @data = {}
80     list.each do |file|
81       next unless file.ext == ext
82       db = loadDBM(file)
83       db = loadBDB(file) if not db
84       db = loadTC(file) if not db
85       if not db
86         puts 'ERROR: unable to load db: %s, last error: %s' % [file.abs, $last_error]
87       else
88         puts 'Loaded: %s [%d values]' % [file.abs, db.length]
89         @data[file.path] = db
90       end
91     end
92   end
93
94   def write(file)
95     File.open(file, 'w') do |f|
96       f.write(Marshal.dump(@data))
97     end
98   end
99
100   private
101
102   def loadDBM(file)
103     path = file.abs
104     begin
105       dbm = DBM.open(path.gsub(/\.[^\.]+$/,''), 0666, DBM::READER)
106       data = dbm.to_hash
107       dbm.close
108     rescue
109       $last_error = "[%s]\n%s" % [$!, $@.join("\n")]
110     end
111     data
112   end
113
114   def loadBDB(file)
115     path = file.abs
116     begin
117       db = BDB::Hash.open(path, nil, 'r')
118       data = {}
119       db.each do |key, value|
120         data[key] = value
121       end
122       db.close
123     rescue
124       $last_error = "[%s]\n%s" % [$!, $@.join("\n")]
125     end
126     data
127   end
128
129   def loadTC(file)
130     path = file.abs
131     begin
132       db = TokyoCabinet::BDB.new
133       db.open(path, TokyoCabinet::BDB::OREADER)
134       data = {}
135       db.each do |key, value|
136         data[key] = value
137       end
138       db.close
139     rescue
140       $last_error = "[%s]\n%s" % [$!, $@.join("\n")]
141     end
142     data
143   end
144 end
145
146 puts 'mode = ' + mode
147 puts 'profile= ' + profile
148 puts 'file = ' + file
149
150 if mode == 'backup'
151
152   backup = Backup.new(profile)
153
154   if File.exists? file
155     puts 'ERROR! Backup file already exists!'
156     exit
157   end
158
159   backup.load '.tdb'
160   backup.write(file)
161
162 else
163
164   registry = File.join(profile, './registry')
165
166   data = Marshal.load File.read(file)
167   data.each_pair do |path, db|
168     path = File.expand_path(File.join(registry, path))
169
170     # create directories:
171     dirs = File.dirname(path).split("/")
172     dirs.length.times { |i|
173       dir = dirs[0,i+1].join("/")+"/"
174       unless File.exist?(dir)
175         puts 'create subdir:'+dir
176         Dir.mkdir(dir)
177       end
178     }
179     path.gsub!(/\.([^\.]+)$/,'')
180
181     if File.exists? path+'.db' or File.exists? path+'.tdb'
182       raise 'error, unable to restore to existing db'
183     end
184
185     puts 'restore to: '+path
186     dbm = DBM.open(path, 0666, DBM::WRCREAT)
187     db.each_pair do |key, value|
188       dbm[key] = value
189     end
190     dbm.close
191   end
192
193 end
194
195