braavos 0.0.7
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/Gemfile +12 -0
- data/Gemfile.lock +44 -0
- data/README.rdoc +113 -0
- data/Rakefile +23 -0
- data/bin/braavos +20 -0
- data/lib/braavos.rb +179 -0
- data/lib/braavos/cli.rb +82 -0
- data/lib/braavos/command.rb +30 -0
- data/lib/braavos/config.rb +48 -0
- data/lib/braavos/parallel.rb +104 -0
- data/lib/braavos/service.rb +48 -0
- data/lib/braavos/service/cassandra.rb +187 -0
- data/lib/braavos/service/elasticsearch.rb +15 -0
- data/lib/braavos/storage.rb +7 -0
- data/lib/braavos/storage/file.rb +48 -0
- data/lib/braavos/storage/s3.rb +41 -0
- data/lib/braavos/storage/storage_base.rb +48 -0
- data/lib/braavos/template.rb +49 -0
- data/lib/braavos/version.rb +3 -0
- data/template/cassandra/clear_snapshot.sh.erb +1 -0
- data/template/cassandra/create_snapshot.sh.erb +1 -0
- data/template/cassandra/dump_schema.sh.erb +4 -0
- data/template/cassandra/system_bundle.sh.erb +18 -0
- data/template/cassandra/table_bundle_restore.sh.erb +44 -0
- data/template/cassandra/table_bundle_upload.sh.erb +27 -0
- data/test/braavos/command_test.rb +34 -0
- data/test/braavos/config_test.rb +66 -0
- data/test/braavos/parallel_test.rb +29 -0
- data/test/braavos/template_test.rb +36 -0
- data/test/template/nested/inner.sh.erb +1 -0
- data/test/template/simple.txt.erb +1 -0
- data/test/test_helper.rb +17 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f48996bb89970d302bac43a156684ad867b2fa13
|
4
|
+
data.tar.gz: 82b44ccb9e8ddc0fea2b01a0ad47a1f45a3d1ce6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 04e073c3cce5c0c7dd0d0a25b26f9a114162e6bce51688f1ed2e20e95bfbdb6cb10c86082ffa43c1af2dd27c184a81250f76ce2d931c2ef0b0cfcb654ed961c8
|
7
|
+
data.tar.gz: 161846c8ee93cb51f3ea55f8b8088e945a636ebb16e51a7234b30dccf5bfacba429c7480608ad8d86daaee1068a58c75d79863306abe686d43030e5a1c148d10
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
aws-sdk (1.33.0)
|
5
|
+
json (~> 1.4)
|
6
|
+
nokogiri (>= 1.4.4)
|
7
|
+
uuidtools (~> 2.1)
|
8
|
+
coderay (1.1.0)
|
9
|
+
docile (1.1.0)
|
10
|
+
fakeweb (1.3.0)
|
11
|
+
gli (2.9.0)
|
12
|
+
json (1.8.1)
|
13
|
+
lockfile (2.1.0)
|
14
|
+
method_source (0.8.2)
|
15
|
+
mini_portile (0.5.3)
|
16
|
+
multi_json (1.8.4)
|
17
|
+
nokogiri (1.6.1)
|
18
|
+
mini_portile (~> 0.5.0)
|
19
|
+
pry (0.9.12.6)
|
20
|
+
coderay (~> 1.0)
|
21
|
+
method_source (~> 0.8)
|
22
|
+
slop (~> 3.4)
|
23
|
+
rake (10.1.1)
|
24
|
+
simplecov (0.8.1)
|
25
|
+
docile (~> 1.1.0)
|
26
|
+
lockfile (>= 2.1.0)
|
27
|
+
multi_json
|
28
|
+
simplecov-html (~> 0.8.0)
|
29
|
+
simplecov-html (0.8.0)
|
30
|
+
slop (3.5.0)
|
31
|
+
sqlite3 (1.3.8)
|
32
|
+
uuidtools (2.1.4)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
aws-sdk
|
39
|
+
fakeweb
|
40
|
+
gli
|
41
|
+
pry
|
42
|
+
rake
|
43
|
+
simplecov
|
44
|
+
sqlite3
|
data/README.rdoc
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
= braavos - Backup/Restore Tool
|
2
|
+
|
3
|
+
== Introduction
|
4
|
+
|
5
|
+
This document covers the implementation of a backup/restore utility named braavos and deployed on Cassandra and ElasticSearch servers.
|
6
|
+
It implements:
|
7
|
+
|
8
|
+
* the management of backup data on S3
|
9
|
+
* Backup operations
|
10
|
+
* Restore operations
|
11
|
+
* Pruning executions
|
12
|
+
* full backup strategies (IMPLEMENTED)
|
13
|
+
* restore capabilities
|
14
|
+
* rewind to previous state
|
15
|
+
* restore fallen node (IMPLEMENTED)
|
16
|
+
* restore from different cluster (IMPLEMENTED)
|
17
|
+
* quick replacement strategy (using ebs volume)
|
18
|
+
* secondary backup strategy (google drive)
|
19
|
+
|
20
|
+
=== Versions Targeted:
|
21
|
+
* Cassandra: 1.2
|
22
|
+
* ElasticSearch: 0.90
|
23
|
+
|
24
|
+
== Terminology
|
25
|
+
|
26
|
+
Service : Cassandra(cass) or ElasticSearch(es)
|
27
|
+
|
28
|
+
Node : A running instance of a service.
|
29
|
+
|
30
|
+
Cluster : A named cluster of nodes for a service.
|
31
|
+
|
32
|
+
|
33
|
+
== Design
|
34
|
+
|
35
|
+
Each service’s backups are maintained independently. They may or may not run on independent or shared hardware. Nodes are numbered and linked to their aws instance id.
|
36
|
+
A backup operation will validate the cluster name, environment and node name before proceeding with any mutation.
|
37
|
+
|
38
|
+
=== Cluster Layout
|
39
|
+
|
40
|
+
cluster.json
|
41
|
+
{
|
42
|
+
"environment": "production",
|
43
|
+
"name": "place_directory",
|
44
|
+
"nodes": {
|
45
|
+
"1": {"instance_id": "i-abcdefed" },
|
46
|
+
"2": {"instance_id": "i-abcdefec" },
|
47
|
+
"3": {"instance_id": "i-abcdefeb" }
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
|
52
|
+
=== Data Layout
|
53
|
+
|
54
|
+
General
|
55
|
+
/backup_bucket/name/environment/service/
|
56
|
+
/backup_bucket/name/environment/service/cluster.json
|
57
|
+
/backup_bucket/name/environment/service/full/datetime/node/
|
58
|
+
/backup_bucket/name/environment/service/full/datetime/node/_COMPLETED
|
59
|
+
|
60
|
+
Cassandra
|
61
|
+
/cassandra/data/node/keyspace/table/place_directory_production-Places-ic-218008.tgz ...
|
62
|
+
/cassandra/full/datetime/node/contents.json
|
63
|
+
/cassandra/full/datetime/node/system.tar.gz
|
64
|
+
/cassandra/full/datetime/node/schema.cql
|
65
|
+
/cassandra/full/datetime/node/ring.txt
|
66
|
+
|
67
|
+
ElasticSearch
|
68
|
+
/elasticsearch/full/datetime/node/cluster_state.local.json
|
69
|
+
/elasticsearch/full/datetime/node/cluster_state.global.json
|
70
|
+
/elasticsearch/full/datetime/node/index_name.restore.sh
|
71
|
+
/elasticsearch/full/datetime/node/index_name.tar.gz
|
72
|
+
|
73
|
+
===Braavos Config
|
74
|
+
|
75
|
+
cassandra.yml
|
76
|
+
name: place_directory
|
77
|
+
environment: production
|
78
|
+
service: cassandra
|
79
|
+
bucket_name: backup_bucket
|
80
|
+
data_loc: /var/lib/cassandra/data
|
81
|
+
sync_loc: /mnt/cassandra_snapshots/latest
|
82
|
+
|
83
|
+
Operations
|
84
|
+
|
85
|
+
Initiate a full backup: (IMPLEMENTED)
|
86
|
+
braavos -c cassandra.yml backup full
|
87
|
+
|
88
|
+
List available backups: (IMPLEMENTED)
|
89
|
+
braavos -c cassandra.yml show
|
90
|
+
Verify backup:
|
91
|
+
braavos -c cassandra.yml verify backup_loc
|
92
|
+
|
93
|
+
Restore backup: (IMPLEMENTED)
|
94
|
+
braavos -c cassandra.yml restore backup_loc restore_loc
|
95
|
+
|
96
|
+
Restore across clusters: (IMPLEMENTED)
|
97
|
+
braavos -c cassandra.yml restore --cluster 'name:environment:node_id' backup_loc restore_loc
|
98
|
+
Example: ./bin/braavos -c config/braavos.yml restore --cluster 'place_directory:production:1' full/20140321 /tmp/restore
|
99
|
+
|
100
|
+
Prune backups:
|
101
|
+
braavos -c cassandra.yml prune
|
102
|
+
|
103
|
+
Sync latest (copy to ebs):
|
104
|
+
braavos sync -c cassandra.yml
|
105
|
+
|
106
|
+
== Mentions
|
107
|
+
|
108
|
+
braavos uses and depends on the following awesome components:
|
109
|
+
|
110
|
+
* {GNU Parallel}[http://www.gnu.org/software/parallel/] - The Command-Line Power Tool: O. Tange (2011)
|
111
|
+
* {GNU coreutils}[http://www.gnu.org/software/coreutils/] - The GNU Core Utils, including timeout.
|
112
|
+
* {s3cmd}[http://s3tools.org/s3cmd] - Command Line S3 Client. Please use version 1.5.0-beta1 or greater.
|
113
|
+
* {Ruby}[https://www.ruby-lang.org/] - A Programmer's Best Friend (including ERB templating)
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << 'test'
|
8
|
+
t.pattern = 'test/**/*_test.rb'
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rdoc/task'
|
13
|
+
desc 'Generate documentation.'
|
14
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
15
|
+
rdoc.rdoc_dir = 'rdoc'
|
16
|
+
rdoc.title = 'Braavos Backup/Restore Administrator'
|
17
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
18
|
+
rdoc.rdoc_files.include('README.rdoc')
|
19
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Default: run unit tests.'
|
23
|
+
task :default => :test
|
data/bin/braavos
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# resolve bin path, ignoring symlinks
|
5
|
+
require 'pathname'
|
6
|
+
bin_file = Pathname.new(__FILE__).realpath
|
7
|
+
|
8
|
+
# add locally vendored gems to libpath
|
9
|
+
gem_dir = File.expand_path('../../vendor/gems', bin_file)
|
10
|
+
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
11
|
+
$:.unshift libdir
|
12
|
+
end
|
13
|
+
|
14
|
+
# add self to libpath
|
15
|
+
$:.unshift File.expand_path('../../lib', bin_file)
|
16
|
+
|
17
|
+
# start up the CLI
|
18
|
+
require 'braavos/cli'
|
19
|
+
exit Braavos::CLI.start(ARGV)
|
20
|
+
|
data/lib/braavos.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
ENV["RACK_ENV"] ||= "development"
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'gli'
|
6
|
+
require 'aws-sdk'
|
7
|
+
|
8
|
+
require 'braavos/version'
|
9
|
+
require 'yaml'
|
10
|
+
require 'json'
|
11
|
+
require 'logger'
|
12
|
+
require 'set'
|
13
|
+
|
14
|
+
class Braavos
|
15
|
+
|
16
|
+
autoload :Command, 'braavos/command'
|
17
|
+
autoload :Config, 'braavos/config'
|
18
|
+
autoload :Parallel, 'braavos/parallel'
|
19
|
+
autoload :Service, 'braavos/service'
|
20
|
+
autoload :Storage, 'braavos/storage'
|
21
|
+
autoload :Template, 'braavos/template'
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def config(config={})
|
25
|
+
@config ||= Config.new(config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def command
|
29
|
+
@command ||= Command.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def parallel
|
33
|
+
@parallel ||= Parallel.new("parallel", config.parallel_jobs || "100%")
|
34
|
+
end
|
35
|
+
|
36
|
+
def template
|
37
|
+
@template ||= Template.new([File.expand_path("../../template", __FILE__)])
|
38
|
+
end
|
39
|
+
|
40
|
+
def service
|
41
|
+
@service ||= config.service_class.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def storage
|
45
|
+
@storage ||= config.storage_class.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def logger
|
49
|
+
@logger ||= Logger.new(STDOUT)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :global_options
|
54
|
+
|
55
|
+
def initialize(options)
|
56
|
+
# Load config
|
57
|
+
Braavos.logger.info "Braavos v#{Braavos::VERSION} Initializing"
|
58
|
+
Braavos.config(YAML.load(ERB.new(File.read(options[:config])).result))
|
59
|
+
|
60
|
+
initialize_aws(options)
|
61
|
+
|
62
|
+
Braavos.logger.level = Logger::INFO
|
63
|
+
if options[:verbose]
|
64
|
+
Braavos.logger.level = Logger::DEBUG
|
65
|
+
end
|
66
|
+
@global_options = options
|
67
|
+
Braavos.logger.debug "Options: #{options}"
|
68
|
+
Braavos.logger.debug "Config: #{Braavos.config.settings}"
|
69
|
+
AWS.config(:logger => Braavos.logger, :log_level => :debug)
|
70
|
+
end
|
71
|
+
|
72
|
+
def backup_full(args, options={})
|
73
|
+
Braavos.storage.verify_cluster(true)
|
74
|
+
if Braavos.service.respond_to? :backup_full
|
75
|
+
Braavos.service.backup_full
|
76
|
+
else
|
77
|
+
raise "Operation not supported by service #{Braavos.config.service}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def restore(args, options={})
|
82
|
+
Braavos.storage.verify_cluster(true)
|
83
|
+
|
84
|
+
# restore location must be empty
|
85
|
+
# backup location must contain _COMPLETED
|
86
|
+
|
87
|
+
if args.length != 2
|
88
|
+
raise "Invalid restore args: backup_loc restore_loc"
|
89
|
+
end
|
90
|
+
|
91
|
+
restore_loc = args[1]
|
92
|
+
if File.exists?(restore_loc) && !File.directory?(restore_loc)
|
93
|
+
raise "restore_loc #{restore_loc} is not a directory"
|
94
|
+
end
|
95
|
+
|
96
|
+
FileUtils.mkdir_p restore_loc
|
97
|
+
|
98
|
+
if Dir["#{restore_loc}/*"].size > 1
|
99
|
+
raise "restore_loc #{restore_loc} directory must be empty"
|
100
|
+
end
|
101
|
+
|
102
|
+
if options[:cluster]
|
103
|
+
if match = options[:cluster].match(/(\w+):(\w+):(\d+)/)
|
104
|
+
name, environment, node = match.captures
|
105
|
+
Braavos.config.name = name
|
106
|
+
Braavos.config.environment = environment
|
107
|
+
Braavos.config.backup_path(true)
|
108
|
+
Braavos.storage.find_node_id(node)
|
109
|
+
else
|
110
|
+
raise "Invalid cluster config"
|
111
|
+
end
|
112
|
+
Braavos.logger.info("Restore across cluster: #{options[:cluster]}")
|
113
|
+
end
|
114
|
+
|
115
|
+
Braavos.service.restore(args[0], args[1])
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
def show(args, options={})
|
120
|
+
Braavos.logger.info "Data ks: #{Braavos.service.keyspaces_data}"
|
121
|
+
Braavos.logger.info "System ks: #{Braavos.service.keyspaces_system}"
|
122
|
+
|
123
|
+
Braavos.storage.verify_cluster(false)
|
124
|
+
node_id = ( options[:node_id] || Braavos.storage.find_node_id )
|
125
|
+
Braavos.logger.info "Node id: #{node_id}"
|
126
|
+
listing = Braavos.storage.list_backups(node_id)
|
127
|
+
puts "AVAILABLE BACKUPS:"
|
128
|
+
listing.each do |g, l|
|
129
|
+
puts
|
130
|
+
puts g
|
131
|
+
puts '-' * g.size
|
132
|
+
if l.size == 0
|
133
|
+
puts "NONE"
|
134
|
+
else
|
135
|
+
l.each do |name|
|
136
|
+
puts "\t#{name}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def verify(args, options={})
|
143
|
+
end
|
144
|
+
|
145
|
+
def prune(args, options={})
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
def sync(args, options={})
|
150
|
+
Braavos.service.sync
|
151
|
+
end
|
152
|
+
|
153
|
+
def cleanup
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
def initialize_aws(options)
|
158
|
+
default_path = File.expand_path("../../config/aws.yml", __FILE__)
|
159
|
+
|
160
|
+
if options[:aws]
|
161
|
+
initialize_aws_from_file(options[:aws])
|
162
|
+
elsif File.exists?(default_path)
|
163
|
+
initialize_aws_from_file(default_path)
|
164
|
+
else
|
165
|
+
initialize_aws_from_environment
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def initialize_aws_from_environment
|
170
|
+
Braavos.config.settings['aws'] = AWS.config.credentials
|
171
|
+
end
|
172
|
+
|
173
|
+
def initialize_aws_from_file(path)
|
174
|
+
raise ArgumentError, "AWS Credentials file not found" unless File.exists?(path)
|
175
|
+
aws_config = YAML.load(ERB.new(File.read(path)).result)
|
176
|
+
AWS.config(aws_config)
|
177
|
+
Braavos.config.settings['aws'] = aws_config
|
178
|
+
end
|
179
|
+
end
|
data/lib/braavos/cli.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'gli'
|
2
|
+
require 'braavos'
|
3
|
+
|
4
|
+
class Braavos::CLI
|
5
|
+
extend GLI::App
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def start(argv)
|
10
|
+
program_desc 'Backup/Restore Administrator'
|
11
|
+
sort_help :manually
|
12
|
+
|
13
|
+
version Braavos::VERSION
|
14
|
+
|
15
|
+
flag [:c,:config], desc: 'Configuration file', arg_name: 'config', required: true
|
16
|
+
flag [:a,:aws], desc: 'AWS Credentials file', arg_name: 'aws', required: false
|
17
|
+
switch [:v,:verbose], desc: 'Verbose mode'
|
18
|
+
|
19
|
+
pre do |global,command,options,args|
|
20
|
+
@braavos = Braavos.new(global)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Execute Backup'
|
25
|
+
command :backup do |c|
|
26
|
+
c.action do |global_options,options,args|
|
27
|
+
@braavos.backup_full(args, options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Execute Restore'
|
32
|
+
command :restore do |c|
|
33
|
+
c.flag [:cluster], desc: 'Restore across cluster. Format: "cluster:environment:node"'
|
34
|
+
|
35
|
+
c.arg_name 'timestamp'
|
36
|
+
c.action do |global_options,options,args|
|
37
|
+
@braavos.restore(args, options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Show Operation'
|
42
|
+
command :show do |c|
|
43
|
+
c.action do |global_options,options,args|
|
44
|
+
@braavos.show(args, options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'Verify Operation'
|
49
|
+
command :verify do |c|
|
50
|
+
c.action do |global_options,options,args|
|
51
|
+
@braavos.verify(args, options)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'Sync Operation'
|
56
|
+
command :sync do |c|
|
57
|
+
c.action do |global_options,options,args|
|
58
|
+
@braavos.sync(args, options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
desc 'Prune Operation'
|
63
|
+
command :prune do |c|
|
64
|
+
c.action do |global_options,options,args|
|
65
|
+
@braavos.prune(args, options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
post do |global,command,options,args|
|
70
|
+
@braavos.cleanup
|
71
|
+
end
|
72
|
+
|
73
|
+
on_error do |exception|
|
74
|
+
@braavos.cleanup if @braavos
|
75
|
+
raise exception unless exception.is_a?(GLI::BadCommandLine)
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
run(argv)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|