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,156 @@
1
+ require "lvm"
2
+ require "singleton"
3
+
4
+ module Snapback
5
+ module App
6
+ class Install
7
+ include Singleton
8
+
9
+ def go
10
+ puts ""
11
+ puts "Hi!"
12
+ puts ""
13
+ puts "I'm going to guide you through setting up Snapback."
14
+ puts "This script will check to see if your environment can run "
15
+ puts "Snapback and will setup configuration files for you."
16
+ puts ""
17
+
18
+ begin
19
+ run "Checking LVM is installed",
20
+ "lvm version"
21
+ rescue
22
+ raise "You do not have LVM installed on this computer. You cannot use snapback."
23
+ end
24
+
25
+ # Get information from LVM
26
+ lvm = LVM::LVM.new({:command => "sudo lvm", :version => "2.02.30"})
27
+ volume_groups = []
28
+ volume_group = nil
29
+
30
+ lvm.volume_groups.each do |vg|
31
+ volume_groups.push(vg)
32
+ end
33
+ puts ""
34
+
35
+ # Decide which Logical volume use to use
36
+ if volume_groups.size == 0 then
37
+ raise "You do not have any volume groups in LVM. Please setup LVM before using Snapback"
38
+ elsif volume_groups.size == 1 then
39
+ volume_group = volume_groups[0];
40
+ puts "You have one volume group named #{volume_group.name.green}."
41
+ puts "Snapback will use this logical volume."
42
+ else
43
+ puts "Here is a list of volume groups on your system:"
44
+
45
+ volume_groups.each do |vg|
46
+ puts "#{volume_groups.size}.\t#{vg.name}"
47
+ end
48
+
49
+ volume_group_number = ask_int "Which volume group would you like Snapback use", volume_groups.size
50
+ volume_group = volume_groups[volume_group_number - 1];
51
+ end
52
+
53
+ puts ""
54
+
55
+ # Check for mount directory
56
+ mysql_mount_dir = ask_string "Where do you want to mount your logical volumes [/mnt/mysql]"
57
+ puts ""
58
+
59
+ if mysql_mount_dir.empty? then
60
+ mysql_mount_dir = "/mnt/mysql"
61
+ end
62
+
63
+ if !check "Checking for directory #{mysql_mount_dir}", lambda {
64
+ File.directory? mysql_mount_dir
65
+ } then
66
+ run "Creating directory #{mysql_mount_dir}",
67
+ "mkdir -p #{mysql_mount_dir}"
68
+
69
+ on_rollback lambda {
70
+ run "Removing #{mysql_mount_dir} directory",
71
+ "rm -rf #{mysql_mount_dir}"
72
+ }
73
+ end
74
+
75
+ # MySQL connection
76
+
77
+ while true
78
+ puts ""
79
+ puts "Enter the crudentials to connect to MySQL"
80
+
81
+ $database = Snapback::Database.instance
82
+
83
+ $database.hostname = ask_string "MySQL hostname [localhost]"
84
+ if $database.hostname.empty? then
85
+ $database.hostname = "localhost"
86
+ end
87
+
88
+ $database.username = ask_string "MySQL username [root]"
89
+ if $database.username.empty? then
90
+ $database.username = "root"
91
+ end
92
+
93
+ $database.password = ask_string "MySQL password"
94
+
95
+ puts ""
96
+
97
+ begin
98
+ check "Connecting to MySQL database", lambda {
99
+ $database.connect
100
+ true
101
+ }
102
+ rescue
103
+ show_failed
104
+ next
105
+ end
106
+
107
+ break
108
+ end
109
+
110
+ if !check "Checking #{"innodb_file_per_table"} is #{"ON"}", lambda {
111
+ $database.innodb_file_per_table
112
+ } then
113
+ debug "Setting #{"innodb_file_per_table"} to #{"ON"}"
114
+ $database.set_innodb_file_per_table true
115
+
116
+ on_rollback lambda {
117
+ debug "Setting #{"innodb_file_per_table"} to #{"OFF"}"
118
+ $database.set_innodb_file_per_table false
119
+ }
120
+ end
121
+
122
+ $config = {
123
+ 'lvm' => {
124
+ 'volume_group' => volume_group.name.to_s,
125
+ 'prefix_database' => 'snapback-active',
126
+ 'prefix_backup' => 'snapback-backup'
127
+ },
128
+
129
+ 'mysql' => {
130
+ 'hostname' => $database.hostname,
131
+ 'username' => $database.username,
132
+ 'password' => $database.password
133
+ },
134
+
135
+ 'filesystem' => {
136
+ 'mount' => '/mnt/mysql'
137
+ }
138
+ }
139
+
140
+ File.open($options[:config], 'w+') { |f|
141
+ f.write($config.to_yaml)
142
+ }
143
+
144
+ on_rollback lambda {
145
+ File.unlink $options[:config]
146
+ }
147
+
148
+ puts ""
149
+ puts "Snapback is now installed and configured."
150
+ puts "To start using Snapback, run the following command: "
151
+ puts ""
152
+ puts "sudo snapback --help".yellow
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,62 @@
1
+ require "singleton"
2
+
3
+ module Snapback
4
+ module App
5
+ class Mount
6
+ include Singleton
7
+
8
+ def go
9
+ volume_group_name = "#{$config['lvm']['volume_group']}"
10
+ logical_volume_name = "#{$config['lvm']['prefix_database']}-#{$options[:database]}"
11
+ mount_dir = get_mount_dir $options[:database]
12
+ mysql_data_dir = $database.get_data_dir
13
+
14
+ exec_flush
15
+
16
+ # Stop the MySQL Server
17
+ $database.server_stop
18
+
19
+ on_rollback lambda {
20
+ $database.server_start
21
+ }
22
+
23
+ # Create a new directory for where the MySQL database can be mounted
24
+ # mkdir /mnt/mysql/{dbName};
25
+
26
+ if !File.directory? mount_dir then
27
+ run "Make mount directory",
28
+ "mkdir #{mount_dir}"
29
+
30
+ on_rollback lambda {
31
+ run "Removing mount directory",
32
+ "rmdir #{$mount_dir}"
33
+ }
34
+
35
+ run "Changing permissions of mount directory",
36
+ "chmod 0777 #{mount_dir}"
37
+ end
38
+
39
+ # # Mount the new logical volume
40
+ # mount /dev/{vgName}/mysql-{dbName} /mnt/mysql/{dbName};
41
+ exec_mount "/dev/#{volume_group_name}/#{logical_volume_name}", mount_dir
42
+
43
+ # # Symbolic-link the MySQL data directory to the new logical volume
44
+ # ln -s /mnt/mysql/{dbName} {mysql-data-dir}/{dbName}/
45
+ exec_link "#{mysql_data_dir}/#{$options[:database]}", mount_dir
46
+
47
+ # # Change the permissions & ownership to MySQL
48
+ # chown -R mysql:mysql {mysql-data-dir}/{dbName}/
49
+ # chown -R mysql:mysql /mnt/mysql/{dbName}/
50
+ exec_chown "#{mysql_data_dir}/#{$options[:database]}"
51
+ exec_chown mount_dir
52
+
53
+ # Stop the MySQL Server
54
+ $database.server_start
55
+
56
+ on_rollback lambda {
57
+ $database.server_stop
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,63 @@
1
+ require "singleton"
2
+
3
+ module Snapback
4
+ module App
5
+ class Rollback
6
+ include Singleton
7
+
8
+ def go
9
+ volume_group_name = "#{$config['lvm']['volume_group']}"
10
+ logical_volume_name = "#{$config['lvm']['prefix_database']}-#{$options[:database]}"
11
+ mount_dir = get_mount_dir $options[:database]
12
+ mysql_data_dir = $database.get_data_dir
13
+
14
+ # Flush the MySQL Logs
15
+ exec_flush
16
+
17
+ # Stop the MySQL Server
18
+ $database.server_stop
19
+
20
+ on_rollback lambda {
21
+ $database.server_start
22
+ }
23
+
24
+ # Remove the symbolic link
25
+ exec_unlink "#{mysql_data_dir}/#{$options[:database]}", mount_dir
26
+
27
+ # Unmount the logical volume
28
+ exec_unmount "/dev/#{volume_group_name}/#{logical_volume_name}", mount_dir
29
+
30
+ # Deactivate the logical volume
31
+ exec_deactivate "/dev/#{volume_group_name}/#{logical_volume_name}"
32
+
33
+ # Merge the old logical volume into the new one
34
+ # lvconvert --merge /dev/{vgName}/backup-{dbName}
35
+ run "Merging the snapshot into the live logical volume",
36
+ "lvconvert --merge /dev/#{volume_group_name}/#{$config['lvm']['prefix_backup']}-#{$options[:database]}"
37
+
38
+ # Active the master drive
39
+ # lvchange -ay /dev/{vgName}/mysql-{dbName}
40
+ exec_activate "/dev/#{volume_group_name}/#{logical_volume_name}"
41
+
42
+ # Mount the logical volume
43
+ exec_mount "/dev/#{volume_group_name}/#{logical_volume_name}", mount_dir
44
+
45
+ # Symbolic-link the MySQL data directory to the new logical volume
46
+ exec_link "#{mysql_data_dir}/#{$options[:database]}", mount_dir
47
+
48
+ # Change the permissions & ownership to MySQL
49
+ # chown -R mysql:mysql {mysql-data-dir}/{dbName}/
50
+ # chown -R mysql:mysql /mnt/mysql/{dbName}/
51
+ exec_chown "#{mysql_data_dir}/#{$options[:database]}"
52
+ exec_chown "#{mount_dir}"
53
+
54
+ # Start the MySQL Server
55
+ $database.server_start
56
+
57
+ on_rollback lambda {
58
+ $database.server_stop
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,74 @@
1
+ require "singleton"
2
+
3
+ module Snapback
4
+ module App
5
+ class Snapshot
6
+ include Singleton
7
+
8
+ def go
9
+ # Ensure we have a size parameter
10
+ if $options[:size].nil? then
11
+ raise "You must specify a size attribute. E.g.: -s 100M"
12
+ end
13
+
14
+ if !$database.db_exists?($options[:database]) then
15
+ raise "Database '#{$options[:database]}' does not exist"
16
+ end
17
+
18
+ volume_group_name = "#{$config['lvm']['volume_group']}"
19
+ logical_volume_name = "#{$config['lvm']['prefix_database']}-#{$options[:database]}"
20
+ mount_dir = get_mount_dir $options[:database]
21
+ mysql_data_dir = $database.get_data_dir
22
+
23
+ # Flush the MySQL Logs
24
+ exec_flush
25
+
26
+ # Stop the MySQL Server
27
+ $database.server_stop
28
+
29
+ on_rollback lambda {
30
+ $database.server_start
31
+ }
32
+
33
+ # Unlink
34
+ exec_unlink "#{mysql_data_dir}/#{$options[:database]}", mount_dir
35
+
36
+ # Unmount
37
+ exec_unmount "/dev/#{volume_group_name}/#{logical_volume_name}", mount_dir
38
+
39
+ # Deactivate
40
+ exec_deactivate "/dev/#{volume_group_name}/#{logical_volume_name}"
41
+
42
+ # Branch the logical volume with a snapshot
43
+ run "Snapshot the logical volume",
44
+ "lvcreate -L #{$options[:size]} -s -n #{$config['lvm']['prefix_backup']}-#{$options[:database]} /dev/#{volume_group_name}/#{logical_volume_name}"
45
+
46
+ on_rollback lambda {
47
+ run "Remove the snapshot",
48
+ "lvremove /dev/#{volume_group_name}/#{$config['lvm']['prefix_backup']}-#{$options[:database]}"
49
+ }
50
+
51
+ # Active the master drive
52
+ exec_activate "/dev/#{volume_group_name}/#{logical_volume_name}"
53
+
54
+ # Mount the master drive
55
+ exec_mount "/dev/#{volume_group_name}/#{logical_volume_name}", mount_dir
56
+
57
+ # Symbolic-link the MySQL data directory to the new logical volume
58
+ # ln -s /mnt/mysql/{dbName} {mysql-data-dir}/{dbName}/
59
+ exec_link "#{mysql_data_dir}/#{$options[:database]}", mount_dir
60
+
61
+ # Change the permissions & ownership to MySQL
62
+ exec_chown "#{mysql_data_dir}/#{$options[:database]}"
63
+ exec_chown mount_dir
64
+
65
+ # Start the MySQL Server
66
+ $database.server_start
67
+
68
+ on_rollback lambda {
69
+ $database.server_stop
70
+ }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,39 @@
1
+ require "singleton"
2
+
3
+ module Snapback
4
+ module App
5
+ class Unmount
6
+ include Singleton
7
+
8
+ def go
9
+ volume_group_name = "#{$config['lvm']['volume_group']}"
10
+ logical_volume_name = "#{$config['lvm']['prefix_database']}-#{$options[:database]}"
11
+ mount_dir = get_mount_dir $options[:database]
12
+ mysql_data_dir = $database.get_data_dir
13
+
14
+ # Flush
15
+ exec_flush
16
+
17
+ # Stop the MySQL Server
18
+ $database.server_stop
19
+
20
+ on_rollback lambda {
21
+ $database.server_start
22
+ }
23
+
24
+ # Unlink
25
+ exec_unlink "#{mysql_data_dir}/#{$options[:database]}", mount_dir
26
+
27
+ # Unmount
28
+ exec_unmount "/dev/#{volume_group_name}/#{logical_volume_name}", mount_dir
29
+
30
+ # Start the MySQL Server
31
+ $database.server_start
32
+
33
+ on_rollback lambda {
34
+ $database.server_stop
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,102 @@
1
+ require 'singleton'
2
+ require 'mysql'
3
+
4
+ # ## Database functions ##
5
+
6
+ module Snapback
7
+ class Database
8
+ include Singleton
9
+
10
+ attr_accessor :hostname, :username, :password
11
+
12
+ @connection = nil
13
+
14
+ @hostname = ""
15
+ @username = ""
16
+ @password = ""
17
+
18
+ def connect
19
+ @connection = Mysql.new @hostname, @username, @password
20
+ end
21
+
22
+ def flush_lock
23
+ begin
24
+ @connection.query "FLUSH TABLES WITH READ LOCK"
25
+ end
26
+
27
+ true
28
+ end
29
+
30
+ def unlock
31
+ begin
32
+ @connection.query "UNLOCK TABLES"
33
+ end
34
+
35
+ true
36
+ end
37
+
38
+ def get_data_dir
39
+ begin
40
+ rs = @connection.query "SHOW VARIABLES LIKE 'datadir'"
41
+ rs.each_hash do |row|
42
+ return row['Value'].gsub(/\/$/, '')
43
+ end
44
+ end
45
+ end
46
+
47
+ def innodb_file_per_table
48
+ begin
49
+ rs = @connection.query "SHOW VARIABLES LIKE 'innodb_file_per_table'"
50
+ rs.each_hash do |row|
51
+ return row['Value'] == "ON"
52
+ end
53
+ rescue
54
+ raise "MySQL Query failed"
55
+ end
56
+ end
57
+
58
+ def set_innodb_file_per_table on
59
+ begin
60
+ rs = @connection.prepare "SET GLOBAL innodb_file_per_table = ?"
61
+ rs.execute(on ? 'ON' : 'OFF')
62
+ return true
63
+ rescue
64
+ raise "MySQL Query failed"
65
+ end
66
+ end
67
+
68
+ def db_exists? database_name
69
+ sql = @connection.list_dbs database_name
70
+ sql.size == 1
71
+ end
72
+
73
+ def db_create database_name
74
+ @connection.query "CREATE DATABASE `#{Mysql.escape_string database_name}`"
75
+ true
76
+ end
77
+
78
+ def db_drop database_name
79
+ @connection.query "DROP DATABASE `#{Mysql.escape_string database_name}`"
80
+ true
81
+ end
82
+
83
+ def db_use database_name
84
+ @connection.select_db database_name
85
+ true
86
+ end
87
+
88
+ def server_stop
89
+ run "Stop MySQL server",
90
+ "service mysql stop"
91
+ true
92
+ end
93
+
94
+ def server_start
95
+ run "Start MySQL server and reconnect",
96
+ "service mysql start"
97
+
98
+ self.connect
99
+ true
100
+ end
101
+ end
102
+ end