snapback 0.0.1

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,208 @@
1
+ require "colorize"
2
+ require "open4"
3
+
4
+ LOCKED_FILES = ['.', '..', 'lost+found']
5
+
6
+ # DSL: on_rollback
7
+ # Add a callback if a method (somewhere) fails
8
+ def on_rollback method
9
+ Snapback::Transaction.instance.add_rollback_method method
10
+ end
11
+
12
+ def run description, command
13
+ puts "#{command}".yellow if $options[:verbose]
14
+ debug "#{description}".ljust(72)
15
+
16
+ begin
17
+ err = ""
18
+ status = Open4::popen4(command) do |pid, stdin, stdout, stderr|
19
+ err = stderr.read
20
+ end
21
+
22
+ if status != 0 then
23
+ raise err
24
+ end
25
+ rescue
26
+ show_failed
27
+ raise $!
28
+ end
29
+
30
+ show_ok
31
+ end
32
+
33
+ def check description, command
34
+ print description.ljust(72)
35
+ result = command.call
36
+ if result == true then
37
+ show_ok
38
+ else
39
+ show_no
40
+ end
41
+
42
+ result
43
+ end
44
+
45
+ def debug *args
46
+ print "#{args}"
47
+ end
48
+
49
+ def show_ok
50
+ puts "[#{" OK ".green}]"
51
+ end
52
+
53
+ def show_no
54
+ puts "[#{" NO ".red}]"
55
+ end
56
+
57
+ def show_failed
58
+ puts "[#{"FAILED".red}]"
59
+ end
60
+
61
+ def ask_int question, max
62
+ number = nil
63
+
64
+ while true
65
+ print "#{question}: "
66
+
67
+ number = $stdin.gets.chomp
68
+
69
+ if !Integer(number) then
70
+ puts "The value you entered is not a number."
71
+ puts ""
72
+ next
73
+ end
74
+
75
+ number = number.to_i
76
+
77
+ if number < 1 || number > max then
78
+ puts "Please enter a number between 1 and #{max}."
79
+ puts ""
80
+ next
81
+ end
82
+
83
+ break
84
+ end
85
+
86
+ number
87
+ end
88
+
89
+ def ask_string question
90
+ print "#{question}: "
91
+ $stdin.gets.chomp
92
+ end
93
+
94
+
95
+ # Move MySQL Files
96
+ def move_mysql_files from_dir, to_dir
97
+ Dir.foreach(from_dir) do |filename|
98
+ if !LOCKED_FILES.include?(filename) then
99
+ run "Moving #{from_dir}/#{filename} to #{to_dir}",
100
+ "mv #{from_dir}/#{filename} #{to_dir}"
101
+ end
102
+ end
103
+ end
104
+
105
+ def get_mount_dir database
106
+ "#{$config['filesystem']['mount']}/#{$config['lvm']['prefix_database']}-#{database}"
107
+ end
108
+
109
+ # Flush table
110
+ def exec_flush
111
+ check "Flushing database to file system with read lock", lambda {
112
+ $database.flush_lock
113
+ }
114
+
115
+ on_rollback lambda {
116
+ check "Removing read lock", lambda { $database.unlock }
117
+ }
118
+ end
119
+
120
+ # Mount the logical volume
121
+ def exec_mount device, directory
122
+ # If it's already mounted, don't worry
123
+ mount_status = Open4::popen4("mountpoint -q #{directory}") do |pid, stdin, stdout, stderr|
124
+ end
125
+
126
+ if mount_status == 0 then
127
+ return
128
+ end
129
+
130
+ # Run command and set rollback
131
+ run "Mounting logical volume",
132
+ "mount #{device} #{directory}"
133
+
134
+ on_rollback lambda {
135
+ run "Unmounting logical volume",
136
+ "umount #{directory}"
137
+ }
138
+ end
139
+
140
+ # Unmount
141
+ def exec_unmount device, directory
142
+ # If it's not already mounted, don't worry
143
+ mount_status = Open4::popen4("mountpoint -q #{directory}") do |pid, stdin, stdout, stderr|
144
+ end
145
+
146
+ if mount_status != 0 then
147
+ return
148
+ end
149
+
150
+ # Run command and set rollback
151
+ run "Unmounting logical volume",
152
+ "umount #{directory}"
153
+
154
+ on_rollback lambda {
155
+ run "Mounting logical volume",
156
+ "mount #{device} #{directory}"
157
+ }
158
+ end
159
+
160
+ # Symbolic-link
161
+ def exec_link entry, exit
162
+ run "Linking symbolic directory",
163
+ "ln -s #{exit} #{entry}"
164
+
165
+ on_rollback lambda {
166
+ run "Unlinking symbolic directory",
167
+ "unlink #{entry}"
168
+ }
169
+ end
170
+
171
+ # Remove symbolic link
172
+ def exec_unlink entry, exit
173
+ run "Unlinking symbolic directory",
174
+ "unlink #{entry}"
175
+
176
+ on_rollback lambda {
177
+ run "Linking symbolic directory",
178
+ "ln -s #{exit} #{entry}"
179
+ }
180
+ end
181
+
182
+ # Activate logical volume
183
+ def exec_activate device
184
+ run "Activating logical volume",
185
+ "lvchange -ay #{device}"
186
+
187
+ on_rollback lambda {
188
+ run "Deactivate logical volume",
189
+ "lvchange -an #{device}"
190
+ }
191
+ end
192
+
193
+ # Activate logical volume
194
+ def exec_deactivate device
195
+ run "Deactivate logical volume",
196
+ "lvchange -an #{device}"
197
+
198
+ on_rollback lambda {
199
+ run "Activating logical volume",
200
+ "lvchange -ay #{device}"
201
+ }
202
+ end
203
+
204
+ # Take ownership
205
+ def exec_chown directory
206
+ run "Changing ownership",
207
+ "chown -R mysql:mysql #{directory}"
208
+ end
@@ -0,0 +1,85 @@
1
+ require 'optparse'
2
+
3
+ module Snapback
4
+ class Options
5
+ def self.parse(args)
6
+ options = {}
7
+
8
+ options[:command] = nil
9
+ options[:database] = nil
10
+
11
+ options[:config] = "#{File.expand_path('~')}/.snapback.yml"
12
+ options[:verbose] = false
13
+ options[:size] = nil
14
+
15
+ opts = OptionParser.new do |opts|
16
+ opts.banner = "Usage: sudo #{$0} [command] [options]"
17
+
18
+ opts.separator "Create database snapshots using logical volume management (LVM)"
19
+ opts.separator ""
20
+ opts.separator "Commands:"
21
+ opts.separator " install".ljust(37) + "Check the environment is sane and setup"
22
+ opts.separator " create DBNAME -s SIZE".ljust(37) + "Create a new snapback compliant database"
23
+ opts.separator " snapshot DBNAME -s SIZE".ljust(37) + "Take a snapshot of the current position"
24
+ opts.separator " commit DBNAME".ljust(37) + "Commit the working copy"
25
+ opts.separator " rollback DBNAME".ljust(37) + "Rollback the working copy to the snapshot"
26
+ opts.separator " drop DBNAME".ljust(37) + "Drop an existing database"
27
+ opts.separator " mount DBNAME".ljust(37) + "Mount an existing database"
28
+ opts.separator " unmount DBNAME".ljust(37) + "Unmount an existing database"
29
+
30
+ opts.separator ""
31
+ opts.separator "Options:"
32
+
33
+ opts.on("-s", "--size SIZE",
34
+ "Allocate a size parameter (e.g.: 1M, 1G, 1T)") do |q|
35
+ options[:size] = q
36
+ end
37
+
38
+ opts.on("-v", "--verbose",
39
+ "Show detailed transaction information") do
40
+ options[:verbose] = true
41
+ end
42
+
43
+ opts.on("-h", "--help",
44
+ "Show this help screen") do
45
+ puts opts
46
+ exit
47
+ end
48
+
49
+ opts.on("-c", "--config",
50
+ "Configuration file") do |q|
51
+ options[:config] = q
52
+ end
53
+
54
+ opts.separator ""
55
+ end
56
+
57
+ opts.parse!(args)
58
+
59
+ case args[0]
60
+ when "install", "create", "snapshot", "commit", "rollback", "drop", "mount", "unmount"
61
+ options[:command] = args[0]
62
+ when nil
63
+ puts opts
64
+ exit
65
+ else
66
+ $stderr.puts "command '#{args[0]}' not recognised"
67
+ puts opts
68
+ exit
69
+ end
70
+
71
+ case options[:command]
72
+ when "create", "snapshot", "commit", "rollback", "drop", "mount", "unmount"
73
+ if args[1].nil? then
74
+ $stderr.puts "you must specify a database with command '#{options[:command]}'"
75
+ puts opts
76
+ exit
77
+ end
78
+
79
+ options[:database] = args[1]
80
+ end
81
+
82
+ options
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,19 @@
1
+ require 'singleton'
2
+
3
+ module Snapback
4
+ class Transaction
5
+ include Singleton
6
+
7
+ @@rollback_commands = []
8
+
9
+ def add_rollback_method method
10
+ @@rollback_commands.push method
11
+ end
12
+
13
+ def do_rollback
14
+ while rollback_command = @@rollback_commands.pop do
15
+ rollback_command.call
16
+ end
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snapback
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Bashkim Isai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2013-06-19 00:00:00 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mysql
16
+ prerelease: false
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 2.9.1
22
+ type: :runtime
23
+ version_requirements: *id001
24
+ - !ruby/object:Gem::Dependency
25
+ name: open4
26
+ prerelease: false
27
+ requirement: &id002 !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.3.0
32
+ type: :runtime
33
+ version_requirements: *id002
34
+ - !ruby/object:Gem::Dependency
35
+ name: ruby-lvm
36
+ prerelease: false
37
+ requirement: &id003 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.1.1
42
+ type: :runtime
43
+ version_requirements: *id003
44
+ - !ruby/object:Gem::Dependency
45
+ name: colorize
46
+ prerelease: false
47
+ requirement: &id004 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 0.5.8
52
+ type: :runtime
53
+ version_requirements: *id004
54
+ description: A simple logical volume database manager
55
+ email:
56
+ executables:
57
+ - snapback
58
+ extensions: []
59
+
60
+ extra_rdoc_files: []
61
+
62
+ files:
63
+ - lib/snapback/app/commit.rb
64
+ - lib/snapback/app/create.rb
65
+ - lib/snapback/app/drop.rb
66
+ - lib/snapback/app/install.rb
67
+ - lib/snapback/app/mount.rb
68
+ - lib/snapback/app/rollback.rb
69
+ - lib/snapback/app/snapshot.rb
70
+ - lib/snapback/app/unmount.rb
71
+ - lib/snapback/database.rb
72
+ - lib/snapback/dsl.rb
73
+ - lib/snapback/options.rb
74
+ - lib/snapback/transaction.rb
75
+ - bin/snapback
76
+ - README.md
77
+ homepage: http://github.com/bashaus/snapback
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+
82
+ post_install_message:
83
+ rdoc_options: []
84
+
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - &id005
90
+ - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - *id005
96
+ requirements: []
97
+
98
+ rubyforge_project:
99
+ rubygems_version: 2.0.3
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Snapback
103
+ test_files: []
104
+