Brackup

 view release on metacpan or  search on metacpan

lib/Brackup/Target/CloudFiles.pm  view on Meta::CPAN

package Brackup::Target::CloudFiles;

use strict;
use warnings;
use base 'Brackup::Target';
use Date::Parse;
use Carp qw(croak);


eval { require Net::Mosso::CloudFiles } or die "You need the Net::Mosso::CloudFiles module installed to use the CloudFiles target in brackup.  Please install this module first.\n\n";

# fields in object:
#   cf  -- Net::Mosso::CloudFiles
#   username
#   apiKey
#   chunkContainer : $self->{username} . "-chunks";
#   backupContainer : $self->{username} . "-backups";
#

sub new {
    my ($class, $confsec) = @_;
    my $self = $class->SUPER::new($confsec);
    
    $self->{username} = $confsec->value("cf_username")
        or die "No 'cf_username'";
    $self->{apiKey} = $confsec->value("cf_api_key")
        or die "No 'cf_api_key'";

	$self->_common_cf_init;

    return $self;
}

sub _common_cf_init {
    my $self = shift;
    $self->{chunkContainerName}  = $self->{username} . "-chunks";
    $self->{backupContainerName} = $self->{username} . "-backups";

    $self->{cf} = Net::Mosso::CloudFiles->new(
		user => $self->{username}, 
		key => $self->{apiKey}
	);

	#createContainer makes the object and returns it, or returns it
	#if it already exists
	$self->{chunkContainer} = 
		$self->{cf}->create_container(name => $self->{chunkContainerName})
			or die "Failed to get chunk container";
	$self->{backupContainer} =
		$self->{cf}->create_container(name => $self->{backupContainerName})
			or die "Failed to get backup container";

}

sub _prompt {
    my ($q) = @_;
    my $ans = <STDIN>;
    $ans =~ s/^\s+//;
    $ans =~ s/\s+$//;
    return $ans;
}

sub new_from_backup_header {
    my ($class, $header, $confsec) = @_;

    my $username  = ($ENV{'CF_USERNAME'} || 
        $confsec->value('cf_username') ||
		_prompt("Your CloudFiles username: "))
        or die "Need your Cloud Files username.\n";

    my $apiKey = ($ENV{'CF_API_KEY'} || 
        $confsec->value('cf_api_key') ||
		_prompt("Your CloudFiles api key: "))
        or die "Need your CloudFiles api key.\n";

    my $self = bless {}, $class;
    $self->{username} = $username;
    $self->{apiKey} = $apiKey;
    $self->_common_cf_init;
    return $self;
}

sub has_chunk {
    my ($self, $chunk) = @_;
    my $dig = $chunk->backup_digest;   # "sha1:sdfsdf" format scalar

    my $res = $self->{chunkContainer}->object(name => $dig);

    return 0 unless $res;

	#return 0 if $@ && $@ =~ /key not found/;

	#TODO: check for content type?
	#return 0 unless $res->{content_type} eq "x-danga/brackup-chunk";
    return 1;
}

sub load_chunk {
    my ($self, $dig) = @_;

    my $val = $self->{chunkContainer}->object(name => $dig)->get
        or return 0;
    return \ $val;
}

sub store_chunk {
    my ($self, $chunk) = @_;
    my $dig = $chunk->backup_digest;
    my $chunkref = $chunk->chunkref;

    my $content = do { local $/; <$chunkref> };

	$self->{chunkContainer}->object(
        name => $dig,
        content_type => 'x-danga/brackup-chunk'
    )->put($content);

	return 1;
}

sub delete_chunk {
    my ($self, $dig) = @_;

	return $self->{chunkContainer}->object(name => $dig)->delete;
}

sub chunks {
    my $self = shift;
	my @objectNames;

	my @objects = $self->{chunkContainer}->objects->all;
	foreach (@objects){ push @objectNames, $_->name;}
	return @objectNames;
}

sub store_backup_meta {
    my ($self, $name, $fh) = @_;

    my $content = do { local $/; <$fh> };

    $self->{backupContainer}->object(name => $name)->put($content);

	return 1;
}

sub backups {
    my $self = shift;

    my @ret;
	
	my @backups = $self->{backupContainer}->objects->all;

    foreach my $backup (@backups) {
        push @ret, Brackup::TargetBackupStatInfo->new(
			$self, $backup->name,
			time => str2time($backup->last_modified),
			size => $backup->size);
    }
    return @ret;
}

sub get_backup {
    my $self = shift;
    my ($name, $output_file) = @_;
	
	my $val = $self->{backupContainer}->object(name => $name)->get
		or return 0;

	$output_file ||= "$name.brackup";
    open(my $out, ">$output_file") or die "Failed to open $output_file: $!\n";

    my $outv = syswrite($out, $val);

    die "download/write error" unless 
		$outv == do { use bytes; length $val };
    close $out;

    return 1;
}

sub delete_backup {
    my $self = shift;
    my $name = shift;
    return $self->{backupContainer}->object(name => $name)->delete;
}


#############################################################
# These functions are for the brackup-verify-inventory script
#############################################################

sub chunkpath {
    my $self = shift;
    my $dig = shift;

    return $dig;
}

sub size {
    my $self = shift;
    my $dig = shift;

    my $obj = $self->{chunkContainer}->object(name => $dig);
    $obj->head();
    my $size = $obj->size;

    return $size;
}


1;

=head1 NAME

Brackup::Target::CloudFiles - backup to Rackspace's CloudFiles Service

=head1 EXAMPLE

In your ~/.brackup.conf file:

  [TARGET:cloudfiles]
  type = CloudFiles
  cf_username  = ...
  cf_api_key =  ....

=head1 CONFIG OPTIONS

=over

=item B<type>

Must be "B<CloudFiles>".

=item B<cf_username>

Your Rackspace/Mosso CloudFiles user name.

=item B<cf_api_key>

Your Rackspace/Mosso CloudFiles api key.

=back

=head1 SEE ALSO

L<Brackup::Target>

L<Net::Mosso::CloudFiles> -- required module to use Brackup::Target::CloudFiles

=head1 AUTHOR

William Wolf E<lt>throughnothing@gmail.comE<gt>




( run in 2.743 seconds using v1.01-cache-2.11-cpan-99c4e6809bf )