lib/backup/storage/cloud_files.rb
require "backup/cloud_io/cloud_files"
module Backup
module Storage
class CloudFiles < Base
include Storage::Cycler
class Error < Backup::Error; end
##
# Rackspace CloudFiles Credentials
attr_accessor :username, :api_key
##
# Rackspace Auth URL (optional)
attr_accessor :auth_url
##
# Rackspace Service Net
# (LAN-based transfers to avoid charges and improve performance)
attr_accessor :servicenet
##
# Rackspace Region (optional)
attr_accessor :region
##
# Rackspace Container Name
attr_accessor :container
##
# Rackspace Container Name for SLO Segments
# Required if #segment_size is set. Must be different from #container.
attr_accessor :segments_container
##
# SLO Segment size, specified in MiB.
#
# Each package file larger than +segment_size+
# will be uploaded as a Static Large Objects (SLO).
#
# Defaults to 0 for backward compatibility (pre v.3.7.0),
# since #segments_container would be required.
#
# Minimum: 1 (0 disables SLO support)
# Maximum: 5120 (5 GiB)
attr_accessor :segment_size
##
# If set, all backup package files (including SLO segments) will be
# scheduled for automatic removal by the server.
#
# The `keep` option should not be used if this is set,
# unless you're transitioning from the `keep` option.
attr_accessor :days_to_keep
##
# Number of times to retry failed operations.
#
# Default: 10
attr_accessor :max_retries
##
# Time in seconds to pause before each retry.
#
# Default: 30
attr_accessor :retry_waitsec
##
# Additional options to pass along to fog.
# e.g. Fog::Storage.new({ :provider => 'Rackspace' }.merge(fog_options))
attr_accessor :fog_options
def initialize(model, storage_id = nil)
super
@servicenet ||= false
@segment_size ||= 0
@max_retries ||= 10
@retry_waitsec ||= 30
@path ||= "backups"
path.sub!(/^\//, "")
check_configuration
end
private
def cloud_io
@cloud_io ||= CloudIO::CloudFiles.new(
username: username,
api_key: api_key,
auth_url: auth_url,
region: region,
servicenet: servicenet,
container: container,
segments_container: segments_container,
segment_size: segment_size,
days_to_keep: days_to_keep,
max_retries: max_retries,
retry_waitsec: retry_waitsec,
fog_options: fog_options
)
end
def transfer!
package.filenames.each do |filename|
src = File.join(Config.tmp_path, filename)
dest = File.join(remote_path, filename)
Logger.info "Storing '#{container}/#{dest}'..."
cloud_io.upload(src, dest)
end
package.no_cycle = true if days_to_keep
end
# Called by the Cycler.
# Any error raised will be logged as a warning.
def remove!(package)
Logger.info "Removing backup package dated #{package.time}..."
remote_path = remote_path_for(package)
objects = cloud_io.objects(remote_path)
raise Error, "Package at '#{remote_path}' not found" if objects.empty?
slo_objects, objects = objects.partition(&:slo?)
cloud_io.delete_slo(slo_objects)
cloud_io.delete(objects)
end
def check_configuration
required = %w[username api_key container]
raise Error, <<-EOS if required.map { |name| send(name) }.any?(&:nil?)
Configuration Error
#{required.map { |name| "##{name}" }.join(", ")} are all required
EOS
raise Error, <<-EOS if segment_size > 0 && segments_container.to_s.empty?
Configuration Error
#segments_container is required if #segment_size is > 0
EOS
raise Error, <<-EOS if container == segments_container
Configuration Error
#container and #segments_container must not be the same container.
EOS
raise Error, <<-EOS if segment_size > 5120
Configuration Error
#segment_size is too large (max 5120)
EOS
end
end
end
end