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.
- checksums.yaml +7 -0
- data/README.md +173 -0
- data/bin/snapback +82 -0
- data/lib/snapback/app/commit.rb +35 -0
- data/lib/snapback/app/create.rb +117 -0
- data/lib/snapback/app/drop.rb +51 -0
- data/lib/snapback/app/install.rb +156 -0
- data/lib/snapback/app/mount.rb +62 -0
- data/lib/snapback/app/rollback.rb +63 -0
- data/lib/snapback/app/snapshot.rb +74 -0
- data/lib/snapback/app/unmount.rb +39 -0
- data/lib/snapback/database.rb +102 -0
- data/lib/snapback/dsl.rb +208 -0
- data/lib/snapback/options.rb +85 -0
- data/lib/snapback/transaction.rb +19 -0
- metadata +104 -0
data/lib/snapback/dsl.rb
ADDED
@@ -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
|
+
|