Class: EbsSnapper::Ebs

Inherits:
Object
  • Object
show all
Defined in:
lib/ebs_snapper/ebs.rb

Defined Under Namespace

Modules: PausingEnumerable Classes: TTL

Constant Summary collapse

DEFAULT_TAG_NAME =
'Snapper'
DEFAULT_PAUSE_TIME =
0

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}, dry_run = false) ⇒ Ebs

Returns a new instance of Ebs.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/ebs_snapper/ebs.rb', line 22

def initialize(opts = {}, dry_run = false)
  @dry_run = dry_run
  @logger = opts[:logger] || Logger.new(STDOUT)
  max_retries = opts[:max_retries] || AWS.config.max_retries
  if !opts[:secret_access_key].nil? && !opts[:access_key_id].nil?
    AWS.config(:access_key_id => opts[:access_key_id],
               :secret_access_key => opts[:secret_access_key],
               :logger => @logger,
               :max_retries => max_retries) 
  else
    AWS.config(:logger => @logger,
               :max_retries => max_retries) 
  end

  @retain = opts[:retain]
  @tag_name = opts[:volume_tag] || DEFAULT_TAG_NAME # default
  PausingEnumerable.pause_time = opts[:pause_time] || DEFAULT_PAUSE_TIME
  @logger.info "Initializing"
  @logger.info {"Dry run mode: #{@dry_run}"}
  @logger.info {"AWS SDK max retries: #{AWS.config.max_retries}"}
end

Instance Method Details

#dry_run?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/ebs_snapper/ebs.rb', line 113

def dry_run?
  @dry_run == true
end

#each_regionObject



148
149
150
151
152
# File 'lib/ebs_snapper/ebs.rb', line 148

def each_region
  ec2.regions.each do |region|
    yield (region)
  end
end

#ec2Object



154
155
156
157
# File 'lib/ebs_snapper/ebs.rb', line 154

def ec2
  @ec2 ||= AWS::EC2.new
  @ec2
end

#purge_old_snapshots(ttl, region, vol_id) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/ebs_snapper/ebs.rb', line 92

def purge_old_snapshots(ttl, region, vol_id)
  snapshots = region.snapshots.filter('volume-id', vol_id).filter('tag-key', @tag_name)
  PausingEnumerable.wrap(snapshots).each do |snapshot|
    unless snapshot.status == :pending
      ts = snapshot.tags[@tag_name]
      if ttl.purge?(ts)
        begin
          if dry_run?
            @logger.info {"Dry run - would have called snapshot.delete for snapshot #{snapshot.id} of volume #{vol_id}"}
          else
            @logger.info {"Purging #{vol_id} snapshot: #{snapshot.id}"}
            snapshot.delete
          end
        rescue => e
          @logger.error "Exception: #{e}\n" + e.backtrace().join("\n")
        end
      end
    end
  end
end

#snapshot_and_purgeObject



44
45
46
47
48
49
50
# File 'lib/ebs_snapper/ebs.rb', line 44

def snapshot_and_purge
  # now snapshot the list
  tagged_volumes.each do |vol_info|
    snapshot_volume(vol_info[:region], vol_info[:volume_id])
    purge_old_snapshots(vol_info[:ttl], vol_info[:region], vol_info[:volume_id])
  end
end

#snapshot_volume(region, vol_id) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/ebs_snapper/ebs.rb', line 74

def snapshot_volume(region, vol_id)
  vol = region.volumes[vol_id]
  if vol != nil
    if dry_run?
      @logger.info {"Dry run - would have called vol.create_snapshot for volume #{vol_id}"}
      @logger.info {"Dry run - would have called snapshot.tag for new snapshot of volume #{vol_id}"}
    else 
      timestamp = Time.now.utc
      @logger.info {"Snapshotting #{vol_id} at: #{timestamp}"}
      snapshot = vol.create_snapshot("Snapper Backup #{timestamp}")
      # tag the snapshot with the timestamp so we can look it up later for cleanup
      snapshot.tag(@tag_name, :value => "#{timestamp.to_i}")
    end
  else
    @logger.error "Error: Volume #{vol_id} in Region: #{region} not found"
  end
end

#tagged_volumesObject



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ebs_snapper/ebs.rb', line 52

def tagged_volumes
  volumes = []
  
  each_region do |r|
    tags = r.tags.filter('resource-type', 'volume').filter('key', @tag_name)
    PausingEnumerable.wrap(tags).each do |tag|
      # if the tag exists, it's using the default retention (TTL)
      ttl_value = @retain
      if tag.value != nil && !tag.value.strip.empty?
        ttl_value = tag.value.strip
      end
      
      volumes << {
        :ttl => TTL.new(ttl_value),
        :region => r,
        :volume_id => tag.resource.id # volume id
      }
    end
  end
  volumes
end