sr-scripts 0.0.2
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/sr-backup-mysql +34 -0
- data/bin/sr-ec2-consistent-snapshot +152 -0
- data/bin/sr-start-slave +117 -0
- data/lib/sr-scripts/version.rb +5 -0
- data/lib/sr-scripts.rb +5 -0
- data/sr-scripts.gemspec +21 -0
- metadata +78 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/sr-backup-mysql
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'fog'
|
5
|
+
require 'mysql'
|
6
|
+
|
7
|
+
connection = Fog::Compute.new(:provider => "AWS", :region => "us-west-1")
|
8
|
+
|
9
|
+
current_instance_id = `curl -s http://169.254.169.254/latest/meta-data/instance-id`
|
10
|
+
|
11
|
+
server = connection.servers.get(current_instance_id)
|
12
|
+
|
13
|
+
disks = server.tags["mysql_disks"].split(":")
|
14
|
+
|
15
|
+
volumes = []
|
16
|
+
server.block_device_mapping.each do |b|
|
17
|
+
if disks.include? b["deviceName"]
|
18
|
+
volumes.push b["volumeId"]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if volumes.length == 0
|
23
|
+
p "No Volumes To Snapshot"
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
#check here that there aren't current pending snapshots
|
28
|
+
pending = connection.snapshots.find_all { |s| s.state == "pending" }.find_all { |s| volumes.include? s.volume_id }.length != 0
|
29
|
+
if pending
|
30
|
+
p "Exiting: Pending Snapshots Exist"
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
#p "./ec2-consistent-snapshot-rb -f conf.yml #{volumes.join(' ')}"
|
34
|
+
`sr-ec2-consistent-snapshot -f /etc/sr-ec2-consistent-snapshot.yml #{volumes.join(' ')}`
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This script was largely ported from ec2-consistent-snapshot written
|
4
|
+
# by Eric Hammond: http://alestic.com/2009/09/ec2-consistent-snapshot
|
5
|
+
# A bunch of his features aren't ported over yet... just the stuff I needed.
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'optparse'
|
9
|
+
require 'fog'
|
10
|
+
|
11
|
+
$opts = {
|
12
|
+
:aws_access_key => ENV["AWS_ACCESS_KEY_ID"],
|
13
|
+
:aws_secret_access_key => ENV["AWS_SECRET_ACCESS_KEY"],
|
14
|
+
:aws_region => 'us-east-1',
|
15
|
+
|
16
|
+
:description => '',
|
17
|
+
:xfs_filesystem => nil,
|
18
|
+
|
19
|
+
:mysql => false,
|
20
|
+
:mysql_username => 'root',
|
21
|
+
:mysql_password => nil,
|
22
|
+
:mysql_host => '127.0.0.1',
|
23
|
+
:mysql_master_status_file => nil,
|
24
|
+
:mysql_slave_status_file => nil,
|
25
|
+
}
|
26
|
+
|
27
|
+
OptionParser.new do |o|
|
28
|
+
o.on("--aws-access-key ACCESS_KEY", "AWS Access Key") {|v| $opts[:aws_access_key] = v }
|
29
|
+
o.on("--aws-secret-access-key SECRET_KEY", "AWS Secret Access Key") {|v| $opts[:aws_secret_access_key] = v }
|
30
|
+
o.on("--aws-region REGION", "AWS Region") {|v| $opts[:aws_region] = v }
|
31
|
+
o.on("--description STRING", "The description for the snapshot") {|v| $opts[:description] = v }
|
32
|
+
o.on("--xfs-filesystem MOUNTPOINT", "Filesystem to be frozen during snapshot") {|v| $opts[:xfs_filesystem] = v }
|
33
|
+
o.on("--mysql", "Indicates that the volume has mysql") {|v| $opts[:mysql] = v }
|
34
|
+
o.on("--mysql-username USERNAME", "MySQL user (default: root)") {|v| $opts[:mysql_username] = v }
|
35
|
+
o.on("--mysql-password PASSWORD", "MySQL password (default: none)") {|v| $opts[:mysql_password] = v }
|
36
|
+
o.on("--mysql-host HOST", "MySQL host (default: 127.0.0.1)") {|v| $opts[:mysql_host] = v }
|
37
|
+
o.on("--mysql-master-status-file FILENAME", "File to store in snapshot with master status") {|v| $opts[:mysql_master_status_file] = v }
|
38
|
+
o.on("--mysql-slave-status-file FILENAME", "File to store in snapshot with slave status") {|v| $opts[:mysql_slave_status_file] = v }
|
39
|
+
o.on("-f", "--configfile PATH", String, "Set config file") do |path|
|
40
|
+
$opts.merge!(Hash[YAML::load(open(path)).map { |k, v| [k.to_sym, v] }])
|
41
|
+
#p Hash[YAML::load(open(path)).map { |k, v| [k.to_sym, v] }]
|
42
|
+
end
|
43
|
+
end.parse!
|
44
|
+
|
45
|
+
p $opts
|
46
|
+
|
47
|
+
if $opts[:aws_access_key].nil? || $opts[:aws_secret_access_key].nil?
|
48
|
+
puts "You must specify your Amazon credentials via --aws-access-key and --aws-secret_access-key"
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
|
52
|
+
if ARGV.empty?
|
53
|
+
puts "You must provide at least one volume id to snapshot"
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
volume_ids = ARGV
|
57
|
+
|
58
|
+
def mysql_locked(&block)
|
59
|
+
mysql = nil
|
60
|
+
|
61
|
+
if $opts[:mysql]
|
62
|
+
require 'mysql'
|
63
|
+
mysql = Mysql::new($opts[:mysql_host], $opts[:mysql_username], $opts[:mysql_password], nil, nil, '/mnt/mysql/mysql/mysql.sock')
|
64
|
+
mysql.query("SET SQL_LOG_BIN=0")
|
65
|
+
mysql.query("FLUSH LOCAL TABLES")
|
66
|
+
mysql.query("FLUSH LOCAL TABLES WITH READ LOCK")
|
67
|
+
|
68
|
+
def query_result_string(mysql, query)
|
69
|
+
result = mysql.query(query)
|
70
|
+
string = ""
|
71
|
+
if result.num_rows() > 0
|
72
|
+
result.fetch_row.each_with_index do |value, i|
|
73
|
+
string << "#{result.fetch_field_direct(i).name}: #{value}\n"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
string
|
77
|
+
end
|
78
|
+
|
79
|
+
if $opts[:mysql_slave_status_file]
|
80
|
+
File.open($opts[:mysql_slave_status_file], "w").puts query_result_string(mysql, "SHOW SLAVE STATUS")
|
81
|
+
end
|
82
|
+
|
83
|
+
rs = mysql.query("SHOW MASTER STATUS")
|
84
|
+
if rs.num_rows() == 1
|
85
|
+
row = rs.fetch_hash
|
86
|
+
master_log_file = row["File"]
|
87
|
+
master_log_pos = row["Position"]
|
88
|
+
master_info = Hash.new
|
89
|
+
master_info["MASTER_LOG_FILE"] = master_log_file
|
90
|
+
master_info["MASTER_LOG_POS"] = master_log_pos
|
91
|
+
end
|
92
|
+
puts master_log_file
|
93
|
+
|
94
|
+
if $opts[:mysql_master_status_file]
|
95
|
+
File.open($opts[:mysql_master_status_file], "w").puts query_result_string(mysql, "SHOW MASTER STATUS")
|
96
|
+
end
|
97
|
+
|
98
|
+
mysql.query("SET SQL_LOG_BIN=1")
|
99
|
+
end
|
100
|
+
|
101
|
+
begin
|
102
|
+
yield master_info
|
103
|
+
ensure
|
104
|
+
mysql.real_query("UNLOCK TABLES") if mysql
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def xfs_frozen(&block)
|
109
|
+
system('xfs_freeze', '-f', $opts[:xfs_filesystem]) if $opts[:xfs_filesystem]
|
110
|
+
|
111
|
+
begin
|
112
|
+
yield
|
113
|
+
ensure
|
114
|
+
system('xfs_freeze', '-u', $opts[:xfs_filesystem]) if $opts[:xfs_filesystem]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
snapshots = []
|
119
|
+
master_info = nil
|
120
|
+
|
121
|
+
connection = Fog::Compute.new(:provider => "AWS", :aws_access_key_id => $opts[:aws_access_key], :aws_secret_access_key => $opts[:aws_secret_access_key], :region => $opts[:aws_region])
|
122
|
+
sdb = Fog::AWS::SimpleDB.new(:aws_access_key_id => $opts[:aws_access_key], :aws_secret_access_key => $opts[:aws_secret_access_key])
|
123
|
+
begin
|
124
|
+
mysql_locked() do |info|
|
125
|
+
master_info = info
|
126
|
+
xfs_frozen() do
|
127
|
+
volume_ids.each do |volume_id|
|
128
|
+
snapshot = connection.snapshots.create(:volume_id => volume_id, :description => $opts[:description])
|
129
|
+
|
130
|
+
snapshots.push(snapshot.id)
|
131
|
+
puts "#{volume_id}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
random_id = rand(36**8).to_s(36)
|
138
|
+
attributes = Hash.new
|
139
|
+
attributes["instance_id"] = `curl http://169.254.169.254/latest/meta-data/instance-id`
|
140
|
+
attributes["snapshots"] = snapshots.join(",")
|
141
|
+
attributes["timestamp"] = Time.now.to_i
|
142
|
+
attributes["master_log_file"] = master_info["MASTER_LOG_FILE"]
|
143
|
+
attributes["master_log_pos"] = master_info["MASTER_LOG_POS"]
|
144
|
+
sdb.put_attributes("db_recovery_info", random_id, attributes)
|
145
|
+
|
146
|
+
snapshots.each do |s|
|
147
|
+
attributes.each do |key, value|
|
148
|
+
connection.tags.create(:resource_id => s, :key => key, :value => value)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
p master_info
|
data/bin/sr-start-slave
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'fog'
|
5
|
+
require 'mysql'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
connection = Fog::Compute.new(:provider => "AWS", :region => "us-west-1")
|
9
|
+
sdb = Fog::AWS::SimpleDB.new()
|
10
|
+
|
11
|
+
# somewhere up here check to see if the devices are already attached
|
12
|
+
|
13
|
+
p connection.snapshots.all.first
|
14
|
+
|
15
|
+
if ARGV.length != 1
|
16
|
+
p "Must specify an instance id"
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
instance_id = ARGV[0]
|
21
|
+
|
22
|
+
records = sdb.select("SELECT * FROM db_recovery_info WHERE instance_id = '#{instance_id}' AND timestamp > '1' ORDER BY timestamp DESC LIMIT 5").body["Items"]
|
23
|
+
|
24
|
+
p records
|
25
|
+
|
26
|
+
rows = []
|
27
|
+
records.each_value do |r|
|
28
|
+
r.each do |k,v|
|
29
|
+
r[k] = v[0]
|
30
|
+
end
|
31
|
+
data = OpenStruct.new(r)
|
32
|
+
rows.push data
|
33
|
+
end
|
34
|
+
|
35
|
+
rows.sort! { |a,b| b.timestamp <=> a.timestamp }
|
36
|
+
|
37
|
+
latest_snapshots = nil
|
38
|
+
rows.each do |r|
|
39
|
+
if latest_snapshots != nil
|
40
|
+
break
|
41
|
+
end
|
42
|
+
pending = false
|
43
|
+
r.snapshots.split(",").each do |snap|
|
44
|
+
if connection.snapshots.get(snap).state == "pending"
|
45
|
+
pending = true
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
if pending == false
|
50
|
+
latest_snapshots = r.snapshots.split(",")
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
#latest_snapshots now equals the snapshot ids
|
56
|
+
p latest_snapshots
|
57
|
+
|
58
|
+
def get_latest_snapshot(snapshots, instance_id)
|
59
|
+
filtered_snap = snapshots.find_all { |s| s.tags["instance_id"] == instance_id }
|
60
|
+
latest_snap = filtered_snap.sort { |a,b| b.created_at <=> a.created_at }.first unless filtered_snap.length == 0
|
61
|
+
return latest_snap
|
62
|
+
end
|
63
|
+
|
64
|
+
snapshot = get_latest_snapshot(connection.snapshots.all, instance_id)
|
65
|
+
|
66
|
+
p snapshot
|
67
|
+
|
68
|
+
# USE THE TAGS in snapshot to call CHANGE MASTER TO
|
69
|
+
|
70
|
+
current_master = connection.servers.get(instance_id)
|
71
|
+
|
72
|
+
# VALIDATE THAT NUMBER OF SNAPSHOTS EQUALS NUMBER OF MYSQL DISKS
|
73
|
+
|
74
|
+
master_disks = current_master.tags["mysql_disks"].split(":")
|
75
|
+
|
76
|
+
if latest_snapshots.length != master_disks.length
|
77
|
+
p "EXITING: Number of Snapshots != Number of Disks"
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
|
81
|
+
p current_master
|
82
|
+
# need to find out the current node that this is running on
|
83
|
+
#
|
84
|
+
#
|
85
|
+
current_instance_id = `curl http://169.254.169.254/latest/meta-data/instance-id`
|
86
|
+
current_instance = connection.servers.get(current_instance_id)
|
87
|
+
|
88
|
+
latest_snapshots.each_index |index| do
|
89
|
+
snapshot_id = latest_snapshots[index]
|
90
|
+
current_disk = master_disks[index]
|
91
|
+
|
92
|
+
snapshot = connection.snapshots.get(snapshot_id)
|
93
|
+
|
94
|
+
vol = connection.volumes.new(:snapshot_id => snapshot_id, :availability_zone => current_instance.availability_zone, :size => snapshot.volume_size)
|
95
|
+
vol.device = current_disk
|
96
|
+
vol.server = current_instance
|
97
|
+
vol.save
|
98
|
+
p vol
|
99
|
+
|
100
|
+
until vol.state == "in-use" do
|
101
|
+
sleep 1
|
102
|
+
vol.reload
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
exit
|
107
|
+
|
108
|
+
sleep 15
|
109
|
+
|
110
|
+
# NEED TO FIND OUT IF NUMBER OF DISKS IS GREATER THAN 1, IF SO, THEN use mdadm to create an array and mount, if not, then just mount
|
111
|
+
|
112
|
+
`mkdir /mnt/mysql`
|
113
|
+
`mount -t xfs /dev/sdk /mnt/mysql`
|
114
|
+
`service mysql start`
|
115
|
+
|
116
|
+
#`mysql -e "CHANGE MASTER TO MASTER_HOST='#{current_master.private_ip_address}', MASTER_LOG_FILE='binarylogs.006693', MASTER_LOG_POS=785268118;"`
|
117
|
+
# start mysql, connect to mysql and issue change master and start slave commands
|
data/lib/sr-scripts.rb
ADDED
data/sr-scripts.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "sr-scripts/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "sr-scripts"
|
7
|
+
s.version = Sr::Scripts::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Davy Campano"]
|
10
|
+
s.email = ["dcampano@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Admin scripts}
|
13
|
+
s.description = %q{Admin scripts}
|
14
|
+
|
15
|
+
s.rubyforge_project = "sr-scripts"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sr-scripts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Davy Campano
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-18 00:00:00 -06:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Admin scripts
|
23
|
+
email:
|
24
|
+
- dcampano@gmail.com
|
25
|
+
executables:
|
26
|
+
- sr-backup-mysql
|
27
|
+
- sr-ec2-consistent-snapshot
|
28
|
+
- sr-start-slave
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- Rakefile
|
37
|
+
- bin/sr-backup-mysql
|
38
|
+
- bin/sr-ec2-consistent-snapshot
|
39
|
+
- bin/sr-start-slave
|
40
|
+
- lib/sr-scripts.rb
|
41
|
+
- lib/sr-scripts/version.rb
|
42
|
+
- sr-scripts.gemspec
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: ""
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project: sr-scripts
|
73
|
+
rubygems_version: 1.5.2
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Admin scripts
|
77
|
+
test_files: []
|
78
|
+
|