Net-XIPCloud

 view release on metacpan or  search on metacpan

XIPCloud.pm  view on Meta::CPAN


  # default values for API and version
  $self->{api_url} = 'https://auth.storage.santa-clara.internapcloud.net:443/';
  $self->{api_version} = 'v1.0';

  # stash remaining arguments in object
  foreach my $el (keys %args) {
    $self->{$el} = $args{$el};
  }
  return $self;
}

=head2 connect()

Connects to XIPCloud using the username and password provids in the new() call.

Method returns 1 for success and undef for failure.

=cut

sub connect() {
  my $self = shift;
  my $status = undef;

  # prepare authentication headers
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(GET => $self->{api_url}.$self->{api_version});
  $req->header( 'X-AUTH-USER' => $self->{username} );
  $req->header( 'X-AUTH-KEY' => $self->{password} );

  # dispatch request
  my $res = $ua->request($req);

  # persist state on connect
  if ($res->is_success) {
    $status = 1;
    $self->{connected} = 1;
    $self->{storage_token} = $res->header( 'x-storage-token' );
    $self->{storage_url} = $res->header( 'x-storage-url' );
    $self->{cdn_url} = $res->header( 'x-cdn-management-url' );
    $self->{debug} && print "connected: token [".$self->{storage_token}."] url [".$self->{storage_url}."] cdn [".$self->{cdn_url}."]\n";
  }

  # fail
  else {
    $self->{debug} && print "connection failed\n";
  }

  return $status;
}

=head2 ls([CONTAINER])

Depending on the calling arguments, this method returns the list of containers or list
of objects within a single container as an array reference.

Limit and marker values may be provided (see API documentation) for pagination.

=cut

sub ls() {
  my $self = shift;
  my $container = shift;
  my $limit = shift;
  my $marker = shift;
  my $list = [];
  my $path = undef;

  # make sure we have an active connection
  return undef unless ($self->{connected});

  # prepare LWP object for connection
  my $ua = LWP::UserAgent->new;
  my $requrl = $self->{storage_url};

  # let caller specify a pseudo path
  if ($container =~ /\//) {
    split('/',$container); 
    $container = shift;
    $path = join('/',@_);
  }

  # we don't necessarily need a container
  # ls() without one lists all the containers
  if ($container) {
    $requrl.='/'.$container;
  }

  # handle special flags
  if ($limit || $marker || $path) {
    $requrl.="?limit=$limit&marker=$marker&path=$path";
  }

  # prepare the request object
  my $req = HTTP::Request->new(GET => $requrl);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );

  # dispatch request
  my $res = $ua->request($req);

  # stuff return values into our result set
  if ($res->is_success) {
    my @raw = split("\n",$res->content);
    foreach (@raw) {
      next if /^$/;
      push @$list, $_;
    }

    $self->{debug} && print "ls: success - got [".scalar(@$list)."] elements\n";
  }

  # failed
  else {
    undef $list;
    $self->{debug} && print "ls: failed\n";
  }

  return $list;
}

=head2 file("somecontainer","someobject")

This call returns metadata about a specific object.

=cut

sub file() {
  my $self = shift;
  my $container = shift;
  my $object = shift;
  my $status = undef;
  my $path = undef;

  # let file() be called with one or two arguments
  if ($object) {
    $container.='/'.$object;
  }

  # handle pseudo paths
  if ($container =~ /\//) {
    split('/',$container);
    $container = shift;
    $path = join('/',@_);
  }

  # ensure we have enough information to proceed
  return undef unless ($self->{connected} && $container && $path);

  # prepare the LWP request
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(HEAD => $self->{storage_url}.'/'.$container.'/'.$path);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );

  # dispatch request
  my $res = $ua->request($req);

  # grab subset of returned fields
  # TODO: should be extended to handle all x- fields
  if ($res->is_success) {
    $status->{size} = $res->header("content-length");
    $status->{mtime} = $res->header("last-modified");
    $status->{md5sum} = $res->header("etag");
    $status->{type} = $res->header("content-type");

    $self->{debug} && print "file: success [$container/$path]\n";
  }

  # fail
  else {
    $self->{debug} && print "file: failed [$container/$path]\n";
  }

  return $status;
}

=head2 cp("fromcontainer","fromobject",'tocontainer","toobject");

Copy the contents of one object to another

=cut

sub cp() {
  my $self = shift;
  my $scontainer = shift;
  my $sobject = shift;
  my $dcontainer = shift;
  my $dobject = shift;
  my $status = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $scontainer && $sobject && $dcontainer && $dobject);

  # hold onto the content-type of the source object for later
  # we'll need it to create the destination object
  my $src = $self->file($scontainer,$sobject);
  return undef unless (ref $src eq 'HASH');
  my $type = $src->{type};

  # prepare the copy request
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(COPY => $self->{storage_url}.'/'.$scontainer.'/'.$sobject);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
  $req->header( 'Destination' => $dcontainer.'/'.$dobject);
  $req->header( 'Content-type' => $type);

  # dispatch the request
  my $res = $ua->request($req);

  # success
  if ($res->is_success) {
    $status = 1;
    $self->{debug} && print "cp: success [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
  }

  # failed
  else {
    $self->{debug} && print "cp: failed [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
  }
  return $status;
}

=head2 mv("fromcontainer","fromobject",'tocontainer","toobject");

Rename an object, clobbering any existing object

=cut

sub mv() {
  my $self = shift;
  my $scontainer = shift;
  my $sobject = shift;
  my $dcontainer = shift;
  my $dobject = shift;
  my $status = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $scontainer && $sobject && $dcontainer && $dobject);

  # exit on moving an objec to itself - bad idea with copy/delete method
  return if ( ($scontainer eq $dcontainer) && ($sobject eq $dobject));

  # get the source object's content-type and save it for later
  my $src = $self->file($scontainer,$sobject);
  return undef unless (ref $src eq 'HASH');
  my $type = $src->{type};

  # prepare the LWP request
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(COPY => $self->{storage_url}.'/'.$scontainer.'/'.$sobject);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
  $req->header( 'Destination' => $dcontainer.'/'.$dobject);
  $req->header( 'Content-type' => $type);

  # dispatch request
  my $res = $ua->request($req);

  # copy was successful
  if ($res->is_success) {

    # delete the old object
    if ( $self->rm($scontainer,$sobject) ) {
      $status = 1;
      $self->{debug} && print "mv: success [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
    }

    # WAT? delete of old object failed!
    else {
      $self->{debug} && print "mv: failed [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
    }
  }

  # copy failed
  else {
    $self->{debug} && print "mv: failed [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
  }
  return $status;
}

=head2 mkdir("somecontainer")

This method creates a new container. It returns 1 for success and undef for failure.

=cut

sub mkdir() {
  my $self = shift;
  my $container = shift;
  my $status = undef;

XIPCloud.pm  view on Meta::CPAN

    $self->{debug} && print "mkdir: failed [$container]\n";
  }

  return $status;
}

=head2 rmdir("somecontainer")

This method removes a container and its contents. It returns 1 for success and undef for failure.

=cut

sub rmdir() {
  my $self = shift;
  my $container = shift;
  my $status = undef;
  my $path = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $container);

  # handle pseudo paths
  if ($container =~ /\//) {
    split('/',$container); 
    $container = shift;
    $path = join('/',@_);
  }

  # TODO - handle recursive deletion of pseudo-folder objects
  # wish there was a way to do this with the api

  # prepare LWP request
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(DELETE => $self->{storage_url}.'/'.$container);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
  $req->header( 'Content-Length' => '0' );

  # dispatch request
  my $res = $ua->request($req);

  # success
  if ($res->is_success) {
    $status = 1;
    $self->{debug} && print "rmdir: success [$container]\n";   
  }

  # failed
  else {
    $self->{debug} && print "rmdir: failed [$container]\n";
  }
  return $status;
}

=head2 du([CONTAINER])

Depending on calling arguments, this method returns account or container-level statistics as 
a hash reference.

=cut

sub du() {
  my $self = shift;
  my $container = shift;
  my $status = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected});

  # prepare LWP reques
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(HEAD => $self->{storage_url}.($container?'/'.$container:''));
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );

  # dispatch request
  my $res = $ua->request($req);

  # success
  if ($res->is_success) {

    # return fields appropriate for container
    if ($container) {
      $status->{bytes} = $res->header('x-container-bytes-used');
      $status->{objects} = $res->header('x-container-object-count');
    }

    # return global statistics
    else {
      $status->{bytes} = $res->header('x-account-bytes-used');
      $status->{objects} = $res->header('x-account-object-count');
      $status->{containers} = $res->header('x-account-container-count');
    }

    $self->{debug} && print "du: success\n";
  }

  # failed
  else{
    $self->{debug} && print "du: failed\n";
  }
  return $status;
}

=head2 get_value("somecontainer","someobject")

This method returns a scalar value, containing the body of the requested object.

=cut

sub get_value() {
  my $self = shift;
  my $container = shift;
  my $object = shift;
  my $data = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $container && $object);

  # prepare the LWP object
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(GET => $self->{storage_url}.'/'.$container.'/'.$object);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );

XIPCloud.pm  view on Meta::CPAN

  # faled
  else {
    $self->{debug} && print "put_file: failed for [$container/$object]\n";
  }
  return $status;
}

=head2 get_fhstream("somecontainer","someobject",*FILE)

This method takes a container, object and open file handle as arguments.
It retrieves the object in chunks, which it writes to *FILE as they are received.

=cut

sub get_fhstream() {
  my $self = shift;
  my $container = shift;
  my $object = shift;
  local (*OUT) = shift;
  my $status = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $container && $object && *OUT);

  # make sure the file handle we were passed is open
  return undef unless ( (O_WRONLY | O_RDWR) & fcntl (OUT, F_GETFL, my $slush));

  # prepare the LWP request
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(GET => $self->{storage_url}.'/'.$container.'/'.$object);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );

  # create our custom handler for reading
  my $res = $ua->request($req,
    sub {
      my ($chunk,$res) = @_;
      print OUT $chunk;
    }
  );

  # success
  if ($res->is_success) {
    $status = 1;

    $self->{debug} && print "get_fhstream: success for [$container/$object]\n";
  }

  # failed
  else {
    $self->{debug} && print "get_fhstream: failed for [$container/$object]\n";
  }
  return $status;
}

=head2 rm("somecontainer","someobject")

This method removes an object.

=cut

sub rm() {
  my $self = shift;
  my $container = shift;
  my $object = shift;
  my $status = undef;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $container && $object);

  # prepare the LWP object
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(DELETE => $self->{storage_url}.'/'.$container.'/'.$object);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
  $req->header( 'Content-Length' => '0' );

  # dispatch the request
  my $res = $ua->request($req);

  # success
  if ($res->is_success) {
    $status = 1;
    $self->{debug} && print "rm: success for [$container/$object]\n";
  }

  # failed
  else {
    $self->{debug} && print "rm: failed for [$container/$object]\n";
  }
  return $status;
}

=head2 create_manifest("somecontainer","someobject")

This method creates a manifest for large-object support

=cut

sub create_manifest() {
  my $self = shift;
  my $container = shift;
  my $object = shift;
  my $status = undef;
  my $content_type = 'application/octet-stream';
  my $data;

  # ensure we have enough information to continue
  return undef unless ($self->{connected} && $container && $object);

  # prepare the LWP request
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(PUT => $self->{storage_url}.'/'.$container.'/'.$object);
  $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
  $req->header( 'Content-type' => $content_type);

  # point the manifest header to ourselves
  # segments will be further along our path:
  # somecontainer/manifest            <- manifest
  # somecontainer/manifest/segment1   <- segment
  # somecontainer/manifest/segment2   <- segment
  $req->header( 'X-Object-Manifest' => $container.'/'.$object);
  $req->header( 'Content-Length' => '0' );



( run in 2.870 seconds using v1.01-cache-2.11-cpan-4ab04211f4c )