Permalink
Browse files

Added lock_and_snapshot command

  • Loading branch information...
1 parent 3d12fdd commit 7ec300cddf89e627fd0521ff03dfcf651dc00989 @octplane committed Sep 6, 2011
Showing with 79 additions and 12 deletions.
  1. +43 −0 bin/lock_and_snapshot.rb
  2. +36 −12 lib/ec2-consistent-backup.rb
@@ -0,0 +1,43 @@
+#!/usr/bin/env ruby
+# Lock a set of disk via the mongo lock command and snapshot them to the cloud
+
+require 'rubygems'
+require 'trollop'
+
+$: << File.join("..", File.dirname(__FILE__), "lib")
+require 'ec2-consistent-backup'
+
+opts = Trollop::options do
+ opt :port, "Mongo port to connect to", :default => 27017
+ opt :access_key_id, "Access Key Id for AWS", :type => :string, :required => true
+ opt :secret_access_key, "Secret Access Key for AWS", :type => :string, :required => true
+ opt :devices, "Devices to snapshot, comma separated", :type => :string, :required => true
+ opt :hostname, "Hostname to look for. Should resolve to a local EC2 Ip", :type => :string, :required => true
+end
+
+# find instance id by
+# - resolving name to ip
+# - looking in EC2 for server
+# Lock Mongo
+# Snapshot
+# Unlock
+
+aki = opts[:access_key_id]
+sak = opts[:secret_access_key]
+
+identifier = EC2InstanceIdentifier.new(aki, sak)
+instance = identifier.get_instance(opts[:hostname])
+
+locker = MongoHelper::DataLocker.new(opts[:port], opts[:hostname])
+
+locker.lock
+begin
+ snapshoter = EC2VolumeSnapshoter.new(aki, sak, instance.id)
+ snapshoter.snapshot_devices(opts[:devices].split(/,/))
+rescue Exception => e
+ require "pp"
+ puts e.inspect
+ pp e.backtrace
+ensure
+ locker.unlock
+end
@@ -1,7 +1,7 @@
-#!/usr/bin/env ruby
require 'rubygems'
require 'mongo'
require 'aws'
+require 'open-uri'
=begin
- check S3 credentials
@@ -43,7 +43,7 @@ def initialize(mdfile = MDFILE)
if md_info =~ /^md([0-9]+) : active ([^ ]+) (.*)$/
set_name = "md#{$1}"
personality = $2
- drives = $3.split(/ /).map{ |i| i.gsub(/\[[0-9]+\]/,'') }.to_a
+ drives = $3.split(/ /).map{ |i| "/dev/"+i.gsub(/\[[0-9]+\]/,'') }.to_a
@set_metadata[set_name] = { :set_name=> set_name, :personality => personality,
:drives => drives}
end
@@ -93,8 +93,8 @@ def which_device(folder)
module MongoHelper
class DataLocker
attr_reader :path
- def initialize(port = 27017)
- @m = Mongo::Connection.new('localhost', port)
+ def initialize(port = 27017, host = 'localhost')
+ @m = Mongo::Connection.new(host, port)
args = @m['admin'].command({'getCmdLineOpts' => 1 })['argv']
p = args.index('--dbpath')
@path = args[p+1]
@@ -124,30 +124,35 @@ def unlock
end
class EC2DeviceHasNoVolume < Exception; end
+
+# This class is responsible of the snapshoting of given disks to EC2
class EC2VolumeSnapshoter
NAME_PREFIX='ECB:'
- def initialize(aki, sak)
+ attr_reader :instance_id
+ # Need access_key_id, secret_access_key and instance_id
+ # If not provided, attempt to fetch current instance_id
+ def initialize(aki, sak, instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").read)
+
+ @instance_id = instance_id
@ec2 = AWS::EC2.new('access_key_id' => aki,
'secret_access_key' => sak)
end
- def snapshot_devices(devices, name ="#{NAME_PREFIX} Snapshot at #{Time.now}")
+ def snapshot_devices(devices, name ="#{NAME_PREFIX}:#{instance_id}")
volumes = {}
devices.each do |device|
volumes[device] = find_volume_for_device(device)
end
volumes.each do |device, volume|
log "Creating snapshot for #{device} on instance #{instance_id}"
- snapshot = volume.create_snapshot(name+" for #{device}")
+ snapshot = volume.create_snapshot(name+" #{device}")
log "Snapshot: #{snapshot.id} started"
end
end
-
def find_volume_for_device(device)
- instance_id = `curl http://169.254.169.254/latest/meta-data/instance-id`
@ec2.volumes.each do |volume|
volume.attachments.each do |attachment|
if attachment.device == device && attachment.instance.id == instance_id
@@ -157,10 +162,27 @@ def find_volume_for_device(device)
end
raise EC2DeviceHasNoVolume.new(device)
end
+end
+class InstanceNotFoundException < Exception; end
+require 'resolv'
+# Fetch an instance from its private ip address
+class EC2InstanceIdentifier
+ # Need access_key_id, secret_access_key
+ def initialize(aki, sak)
+ @ec2 = AWS::EC2.new('access_key_id' => aki,
+ 'secret_access_key' => sak)
+ end
+ # Returns the instance corresponding to the provided hostname
+ def get_instance(hostname)
+ ip = Resolv.getaddress(hostname)
+ instance = @ec2.instances.find { |i| i.private_ip_address == ip }
+ raise InstanceNotFoundException.new(hostname) if instance == nil
+ return instance
+ end
end
-# Logger to stderr
+
def log s
$stderr.puts "[#{Time.now}]: #{s}"
end
@@ -169,8 +191,8 @@ def log s
require 'trollop'
opts = Trollop::options do
opt :port, "Mongo port to connect to", :default => 27017
- opt :access_key_id, "Access Key Id for AWS", :type => :string
- opt :secret_access_key, "Secret Access Key for AWS", :type => :string
+ opt :access_key_id, "Access Key Id for AWS", :type => :string, :required => true
+ opt :secret_access_key, "Secret Access Key for AWS", :type => :string, :required => true
end
# connect to the local mongo
@@ -205,7 +227,9 @@ def log s
e = EC2VolumeSnapshoter.new(opts[:access_key_id], opts[:secret_access_key])
e.snapshot_devices(drives)
rescue Exception => e
+ require "pp"
puts e.inspect
+ pp e.backtrace
ensure
m.unlock
log "Unlocked mongo"

0 comments on commit 7ec300c

Please sign in to comment.