snapback 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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