File Coverage

blib/lib/Net/XIPCloud.pm
Criterion Covered Total %
statement 33 432 7.6
branch 0 218 0.0
condition 0 114 0.0
subroutine 9 28 32.1
pod 18 18 100.0
total 60 810 7.4


line stmt bran cond sub pod time code
1             package Net::XIPCloud;
2              
3 1     1   769 use strict;
  1         2  
  1         36  
4 1     1   6 use Fcntl;
  1         1  
  1         558  
5 1     1   1363 use Data::Dumper;
  1         11606  
  1         79  
6 1     1   9 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         1  
  1         105  
7 1     1   1066 use File::stat;
  1         8476  
  1         7  
8 1     1   3707 use LWP::UserAgent;
  1         167973  
  1         48  
9 1     1   13 use HTTP::Request;
  1         4  
  1         26  
10 1     1   2105 use IO::Socket::SSL;
  1         139702  
  1         9  
11             require Exporter;
12              
13             our $VERSION = '0.7';
14              
15             @ISA = qw(Exporter);
16             @EXPORT = qw();
17              
18             =head1 NAME
19              
20             Net::XIPCloud - Perl extension for interacting with Internap's XIPCloud storage platform
21              
22             =head1 SYNOPSIS
23              
24             use Net::XIPCloud;
25              
26             my $xip = Net::XIPCloud->new( username => 'myusername', password => 'mypassword );
27              
28             $xip->connect();
29              
30             $xip->cp("fromcontainer","fromobject","tocontainer","toobject");
31              
32             $xip->mv("fromcontainer","fromobject","tocontainer","toobject");
33              
34             $xip->file("somecontainer","someobject");
35              
36             $xip->file("somecontainer/some/pseudo/path/to/object");
37              
38             $xip->ls();
39              
40             $xip->ls("mycontainer");
41              
42             $xip->ls("mycontainer/some/pseudo/path/");
43              
44             $xip->mkdir("newcontainer");
45              
46             $xip->mkdir("newcontainer/some/pseudo/path/");
47              
48             $xip->rmdir("somecontainer");
49              
50             $xip->du();
51              
52             $xip->du("somecontainer");
53              
54             my $data = $xip->get_value("somecontainer","someobject");
55              
56             $xip->get_file("somecontainer","someobject","/tmp/someobject");
57              
58             $xip->put_value("somecontainer","someobject",$data,"text/html");
59              
60             $xip->put_file("somecontainer","someobject","/tmp/someobject","text/html");
61              
62             $xip->get_fhstream("somecontainer","someobject",*STDOUT);
63              
64             $xip->rm("somecontainer","someobject");
65              
66             $xip->create_manifest("somecontainer","someobject");
67              
68             $xip->chmod("somecontainer","public");
69              
70             $xip->cdn("somecontainer","enable","logs",300);
71              
72             $xip->cdn("somecontainer","disable");
73              
74             $xip->cdn("somecontainer");
75              
76             $xip->cdn();
77              
78             =head1 DESCRIPTION
79              
80             This perl module creates an XIPCloud object, which allows direct manipulation of objects and containers
81             within Internap's XIPCloud storage.
82              
83             A valid XIPCloud account is required to use this module
84              
85             =cut
86              
87             =head2 new( username => 'username', password => 'password');
88              
89             Returns a reference to a new XIPCloud object. This method requires passing of a valid username and password.
90              
91             =cut
92              
93             sub new() {
94 1     1 1 100 my $class = shift;
95 1         8 my %args = @_;
96 1         4 my $self = {};
97              
98 1         3 bless $self, $class;
99              
100             # default values for API and version
101 1         11 $self->{api_url} = 'https://auth.storage.santa-clara.internapcloud.net:443/';
102 1         4 $self->{api_version} = 'v1.0';
103              
104             # stash remaining arguments in object
105 1         7 foreach my $el (keys %args) {
106 2         8 $self->{$el} = $args{$el};
107             }
108 1         167 return $self;
109             }
110              
111             =head2 connect()
112              
113             Connects to XIPCloud using the username and password provids in the new() call.
114              
115             Method returns 1 for success and undef for failure.
116              
117             =cut
118              
119             sub connect() {
120 0     0 1   my $self = shift;
121 0           my $status = undef;
122              
123             # prepare authentication headers
124 0           my $ua = LWP::UserAgent->new;
125 0           my $req = HTTP::Request->new(GET => $self->{api_url}.$self->{api_version});
126 0           $req->header( 'X-AUTH-USER' => $self->{username} );
127 0           $req->header( 'X-AUTH-KEY' => $self->{password} );
128              
129             # dispatch request
130 0           my $res = $ua->request($req);
131              
132             # persist state on connect
133 0 0         if ($res->is_success) {
134 0           $status = 1;
135 0           $self->{connected} = 1;
136 0           $self->{storage_token} = $res->header( 'x-storage-token' );
137 0           $self->{storage_url} = $res->header( 'x-storage-url' );
138 0           $self->{cdn_url} = $res->header( 'x-cdn-management-url' );
139 0 0         $self->{debug} && print "connected: token [".$self->{storage_token}."] url [".$self->{storage_url}."] cdn [".$self->{cdn_url}."]\n";
140             }
141              
142             # fail
143             else {
144 0 0         $self->{debug} && print "connection failed\n";
145             }
146              
147 0           return $status;
148             }
149              
150             =head2 ls([CONTAINER])
151              
152             Depending on the calling arguments, this method returns the list of containers or list
153             of objects within a single container as an array reference.
154              
155             Limit and marker values may be provided (see API documentation) for pagination.
156              
157             =cut
158              
159             sub ls() {
160 0     0 1   my $self = shift;
161 0           my $container = shift;
162 0           my $limit = shift;
163 0           my $marker = shift;
164 0           my $list = [];
165 0           my $path = undef;
166              
167             # make sure we have an active connection
168 0 0         return undef unless ($self->{connected});
169              
170             # prepare LWP object for connection
171 0           my $ua = LWP::UserAgent->new;
172 0           my $requrl = $self->{storage_url};
173              
174             # let caller specify a pseudo path
175 0 0         if ($container =~ /\//) {
176 0           split('/',$container);
177 0           $container = shift;
178 0           $path = join('/',@_);
179             }
180              
181             # we don't necessarily need a container
182             # ls() without one lists all the containers
183 0 0         if ($container) {
184 0           $requrl.='/'.$container;
185             }
186              
187             # handle special flags
188 0 0 0       if ($limit || $marker || $path) {
      0        
189 0           $requrl.="?limit=$limit&marker=$marker&path=$path";
190             }
191              
192             # prepare the request object
193 0           my $req = HTTP::Request->new(GET => $requrl);
194 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
195              
196             # dispatch request
197 0           my $res = $ua->request($req);
198              
199             # stuff return values into our result set
200 0 0         if ($res->is_success) {
201 0           my @raw = split("\n",$res->content);
202 0           foreach (@raw) {
203 0 0         next if /^$/;
204 0           push @$list, $_;
205             }
206              
207 0 0         $self->{debug} && print "ls: success - got [".scalar(@$list)."] elements\n";
208             }
209              
210             # failed
211             else {
212 0           undef $list;
213 0 0         $self->{debug} && print "ls: failed\n";
214             }
215              
216 0           return $list;
217             }
218              
219             =head2 file("somecontainer","someobject")
220              
221             This call returns metadata about a specific object.
222              
223             =cut
224              
225             sub file() {
226 0     0 1   my $self = shift;
227 0           my $container = shift;
228 0           my $object = shift;
229 0           my $status = undef;
230 0           my $path = undef;
231              
232             # let file() be called with one or two arguments
233 0 0         if ($object) {
234 0           $container.='/'.$object;
235             }
236              
237             # handle pseudo paths
238 0 0         if ($container =~ /\//) {
239 0           split('/',$container);
240 0           $container = shift;
241 0           $path = join('/',@_);
242             }
243              
244             # ensure we have enough information to proceed
245 0 0 0       return undef unless ($self->{connected} && $container && $path);
      0        
246              
247             # prepare the LWP request
248 0           my $ua = LWP::UserAgent->new;
249 0           my $req = HTTP::Request->new(HEAD => $self->{storage_url}.'/'.$container.'/'.$path);
250 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
251              
252             # dispatch request
253 0           my $res = $ua->request($req);
254              
255             # grab subset of returned fields
256             # TODO: should be extended to handle all x- fields
257 0 0         if ($res->is_success) {
258 0           $status->{size} = $res->header("content-length");
259 0           $status->{mtime} = $res->header("last-modified");
260 0           $status->{md5sum} = $res->header("etag");
261 0           $status->{type} = $res->header("content-type");
262              
263 0 0         $self->{debug} && print "file: success [$container/$path]\n";
264             }
265              
266             # fail
267             else {
268 0 0         $self->{debug} && print "file: failed [$container/$path]\n";
269             }
270              
271 0           return $status;
272             }
273              
274             =head2 cp("fromcontainer","fromobject",'tocontainer","toobject");
275              
276             Copy the contents of one object to another
277              
278             =cut
279              
280             sub cp() {
281 0     0 1   my $self = shift;
282 0           my $scontainer = shift;
283 0           my $sobject = shift;
284 0           my $dcontainer = shift;
285 0           my $dobject = shift;
286 0           my $status = undef;
287              
288             # ensure we have enough information to continue
289 0 0 0       return undef unless ($self->{connected} && $scontainer && $sobject && $dcontainer && $dobject);
      0        
      0        
      0        
290              
291             # hold onto the content-type of the source object for later
292             # we'll need it to create the destination object
293 0           my $src = $self->file($scontainer,$sobject);
294 0 0         return undef unless (ref $src eq 'HASH');
295 0           my $type = $src->{type};
296              
297             # prepare the copy request
298 0           my $ua = LWP::UserAgent->new;
299 0           my $req = HTTP::Request->new(COPY => $self->{storage_url}.'/'.$scontainer.'/'.$sobject);
300 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
301 0           $req->header( 'Destination' => $dcontainer.'/'.$dobject);
302 0           $req->header( 'Content-type' => $type);
303              
304             # dispatch the request
305 0           my $res = $ua->request($req);
306              
307             # success
308 0 0         if ($res->is_success) {
309 0           $status = 1;
310 0 0         $self->{debug} && print "cp: success [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
311             }
312              
313             # failed
314             else {
315 0 0         $self->{debug} && print "cp: failed [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
316             }
317 0           return $status;
318             }
319              
320             =head2 mv("fromcontainer","fromobject",'tocontainer","toobject");
321              
322             Rename an object, clobbering any existing object
323              
324             =cut
325              
326             sub mv() {
327 0     0 1   my $self = shift;
328 0           my $scontainer = shift;
329 0           my $sobject = shift;
330 0           my $dcontainer = shift;
331 0           my $dobject = shift;
332 0           my $status = undef;
333              
334             # ensure we have enough information to continue
335 0 0 0       return undef unless ($self->{connected} && $scontainer && $sobject && $dcontainer && $dobject);
      0        
      0        
      0        
336              
337             # exit on moving an objec to itself - bad idea with copy/delete method
338 0 0 0       return if ( ($scontainer eq $dcontainer) && ($sobject eq $dobject));
339              
340             # get the source object's content-type and save it for later
341 0           my $src = $self->file($scontainer,$sobject);
342 0 0         return undef unless (ref $src eq 'HASH');
343 0           my $type = $src->{type};
344              
345             # prepare the LWP request
346 0           my $ua = LWP::UserAgent->new;
347 0           my $req = HTTP::Request->new(COPY => $self->{storage_url}.'/'.$scontainer.'/'.$sobject);
348 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
349 0           $req->header( 'Destination' => $dcontainer.'/'.$dobject);
350 0           $req->header( 'Content-type' => $type);
351              
352             # dispatch request
353 0           my $res = $ua->request($req);
354              
355             # copy was successful
356 0 0         if ($res->is_success) {
357              
358             # delete the old object
359 0 0         if ( $self->rm($scontainer,$sobject) ) {
360 0           $status = 1;
361 0 0         $self->{debug} && print "mv: success [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
362             }
363              
364             # WAT? delete of old object failed!
365             else {
366 0 0         $self->{debug} && print "mv: failed [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
367             }
368             }
369              
370             # copy failed
371             else {
372 0 0         $self->{debug} && print "mv: failed [$scontainer/$sobject]=>[$dcontainer/$dobject]\n";
373             }
374 0           return $status;
375             }
376              
377             =head2 mkdir("somecontainer")
378              
379             This method creates a new container. It returns 1 for success and undef for failure.
380              
381             =cut
382              
383             sub mkdir() {
384 0     0 1   my $self = shift;
385 0           my $container = shift;
386 0           my $status = undef;
387 0           my $path = undef;
388              
389             # ensure we have enough information to proceed
390 0 0 0       return undef unless ($self->{connected} && $container);
391              
392             # handle pseudo paths
393 0 0         if ($container =~ /\//) {
394 0           split('/',$container);
395 0           $container = shift;
396 0           $path = join('/',@_);
397             }
398              
399             # prepare the LWP request
400 0           my $ua = LWP::UserAgent->new;
401 0           my $req = HTTP::Request->new(PUT => $self->{storage_url}.'/'.$container);
402 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
403 0           $req->header( 'Content-Length' => '0' );
404              
405             # dispatch request
406 0           my $res = $ua->request($req);
407              
408             # success
409 0 0         if ($res->is_success) {
410              
411             # create the pseudo path marker if needed
412 0 0         if ($path) {
413 0           $status = $self->put_value($container,$path,' ','application/directory');
414              
415             # success
416 0 0         if ($status) {
417 0 0         $self->{debug} && print "mkdir: success [$container]\n";
418             }
419              
420             # pseudo path failed
421             else {
422 0 0         $self->{debug} && print "mkdir: failed [$container]\n";
423             }
424             }
425             }
426            
427             # failed
428             else {
429 0 0         $self->{debug} && print "mkdir: failed [$container]\n";
430             }
431              
432 0           return $status;
433             }
434              
435             =head2 rmdir("somecontainer")
436              
437             This method removes a container and its contents. It returns 1 for success and undef for failure.
438              
439             =cut
440              
441             sub rmdir() {
442 0     0 1   my $self = shift;
443 0           my $container = shift;
444 0           my $status = undef;
445 0           my $path = undef;
446              
447             # ensure we have enough information to continue
448 0 0 0       return undef unless ($self->{connected} && $container);
449              
450             # handle pseudo paths
451 0 0         if ($container =~ /\//) {
452 0           split('/',$container);
453 0           $container = shift;
454 0           $path = join('/',@_);
455             }
456              
457             # TODO - handle recursive deletion of pseudo-folder objects
458             # wish there was a way to do this with the api
459              
460             # prepare LWP request
461 0           my $ua = LWP::UserAgent->new;
462 0           my $req = HTTP::Request->new(DELETE => $self->{storage_url}.'/'.$container);
463 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
464 0           $req->header( 'Content-Length' => '0' );
465              
466             # dispatch request
467 0           my $res = $ua->request($req);
468              
469             # success
470 0 0         if ($res->is_success) {
471 0           $status = 1;
472 0 0         $self->{debug} && print "rmdir: success [$container]\n";
473             }
474              
475             # failed
476             else {
477 0 0         $self->{debug} && print "rmdir: failed [$container]\n";
478             }
479 0           return $status;
480             }
481              
482             =head2 du([CONTAINER])
483              
484             Depending on calling arguments, this method returns account or container-level statistics as
485             a hash reference.
486              
487             =cut
488              
489             sub du() {
490 0     0 1   my $self = shift;
491 0           my $container = shift;
492 0           my $status = undef;
493              
494             # ensure we have enough information to continue
495 0 0         return undef unless ($self->{connected});
496              
497             # prepare LWP reques
498 0           my $ua = LWP::UserAgent->new;
499 0 0         my $req = HTTP::Request->new(HEAD => $self->{storage_url}.($container?'/'.$container:''));
500 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
501              
502             # dispatch request
503 0           my $res = $ua->request($req);
504              
505             # success
506 0 0         if ($res->is_success) {
507              
508             # return fields appropriate for container
509 0 0         if ($container) {
510 0           $status->{bytes} = $res->header('x-container-bytes-used');
511 0           $status->{objects} = $res->header('x-container-object-count');
512             }
513              
514             # return global statistics
515             else {
516 0           $status->{bytes} = $res->header('x-account-bytes-used');
517 0           $status->{objects} = $res->header('x-account-object-count');
518 0           $status->{containers} = $res->header('x-account-container-count');
519             }
520              
521 0 0         $self->{debug} && print "du: success\n";
522             }
523              
524             # failed
525             else{
526 0 0         $self->{debug} && print "du: failed\n";
527             }
528 0           return $status;
529             }
530              
531             =head2 get_value("somecontainer","someobject")
532              
533             This method returns a scalar value, containing the body of the requested object.
534              
535             =cut
536              
537             sub get_value() {
538 0     0 1   my $self = shift;
539 0           my $container = shift;
540 0           my $object = shift;
541 0           my $data = undef;
542              
543             # ensure we have enough information to continue
544 0 0 0       return undef unless ($self->{connected} && $container && $object);
      0        
545              
546             # prepare the LWP object
547 0           my $ua = LWP::UserAgent->new;
548 0           my $req = HTTP::Request->new(GET => $self->{storage_url}.'/'.$container.'/'.$object);
549 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
550              
551             # dispatch request
552 0           my $res = $ua->request($req);
553              
554             # success
555 0 0         if ($res->is_success) {
556              
557             # stash return data
558 0           $data = $res->content;
559              
560 0 0         $self->{debug} && print "get_value: success for [$container/$object]\n";
561             }
562            
563             # failed
564             else {
565 0 0         $self->{debug} && print "get_value: failed for [$container/$object]\n";
566             }
567 0           return $data;
568             }
569              
570             =head2 put_value("somecontainer","someobject","..data..","text/html")
571              
572             This method places the contents of a passed scalar into the specified container and object.
573              
574             Content-type may be specified, but is optional. It defaults to "text/plain"
575              
576             =cut
577              
578             sub put_value() {
579 0     0 1   my $self = shift;
580 0           my $container = shift;
581 0           my $object = shift;
582 0           my $data = shift;
583 0           my $content_type = shift;
584 0           my $status = undef;
585              
586             # ensure we have enough information to continue
587 0 0 0       return undef unless ($self->{connected} && $container && $object && $data);
      0        
      0        
588              
589             # use a sane default content-type if one isn't specified
590 0 0         unless ($content_type) {
591 0           $content_type = 'application/octet-stream';
592             }
593              
594             # prepare the LWP request
595 0           my $ua = LWP::UserAgent->new;
596 0           my $req = HTTP::Request->new(PUT => $self->{storage_url}.'/'.$container.'/'.$object);
597 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
598 0           $req->header( 'Content-type' => $content_type);
599 0           $req->content( $data );
600              
601             # dispatch request
602 0           my $res = $ua->request($req);
603              
604             # success
605 0 0         if ($res->is_success) {
606            
607             # stash return data
608 0           $data = $res->content;
609              
610 0 0         $self->{debug} && print "put_value: success for [$container/$object]\n";
611             }
612              
613             # failed
614             else {
615 0 0         $self->{debug} && print "put_value: failed for [$container/$object]\n";
616             }
617 0           return $status;
618             }
619              
620             =head2 get_file("somecontainer","someobject","/tmp/destfile")
621              
622             This method places the contents of the requested object in a target location of the filesystem.
623              
624             =cut
625              
626             sub get_file() {
627 0     0 1   my $self = shift;
628 0           my $container = shift;
629 0           my $object = shift;
630 0           my $tmpfile = shift;
631 0           my $status = undef;
632              
633             # ensure we have enough information to continue
634 0 0 0       return undef unless ($self->{connected} && $container && $object && $tmpfile);
      0        
      0        
635              
636             # prepare the LWP request
637 0           my $ua = LWP::UserAgent->new;
638 0           my $req = HTTP::Request->new(GET => $self->{storage_url}.'/'.$container.'/'.$object);
639 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
640              
641             # dispatch request, specifying a location to store the data returned
642 0           my $res = $ua->request($req,$tmpfile);
643              
644             # success
645 0 0         if ($res->is_success) {
646 0           $status = 1;
647              
648 0 0         $self->{debug} && print "get_file: success for [$container/$object]\n";
649             }
650              
651             # failure
652             else {
653 0 0         $self->{debug} && print "get_file: failed for [$container/$object]\n";
654             }
655 0           return $status;
656             }
657              
658             =head2 put_file("somecontainer","someobject","/tmp/sourcefile","text/html")
659              
660             This method places the contents of a specified source file into an object.
661              
662             =cut
663              
664             sub put_file() {
665 0     0 1   my $self = shift;
666 0           my $container = shift;
667 0           my $object = shift;
668 0           my $srcfile = shift;
669 0           my $content_type = shift;
670 0           my $status = undef;
671              
672             # ensure we have enough information to continue
673             # source file must exist
674 0 0 0       return undef unless ($self->{connected} && $container && $object && (-e $srcfile) );
      0        
      0        
675              
676             # use a sane default content-type when one isn't specified
677 0 0         unless ($content_type) {
678 0           $content_type = 'application/octet-stream';
679             }
680              
681             # get the size of the source file
682 0           my $size = stat($srcfile)->size;
683              
684             # open the file in binary mode for reading
685 0           open(IN, $srcfile);
686 0           binmode IN;
687              
688             # create reader callback
689             my $reader = sub {
690 0     0     read IN, my $buf, 65536;
691 0           return $buf;
692 0           };
693              
694             # prepare LWP for fancy stuff
695 0           $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1;
696              
697             # create LWP object
698 0           my $ua = LWP::UserAgent->new;
699 0           my $req = HTTP::Request->new(PUT => $self->{storage_url}.'/'.$container.'/'.$object);
700 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
701 0           $req->header( 'Content-type' => $content_type);
702 0           $req->header( 'Content-length' => $size );
703              
704             # tell LWP to use our reader callback
705 0           $req->content($reader);
706              
707             # dispatch request
708 0           my $res = $ua->request($req);
709              
710             # close input file
711 0           close(IN);
712              
713             # success
714 0 0         if ($res->is_success) {
715 0           $status = 1;
716              
717 0 0         $self->{debug} && print "put_file: success for [$container/$object]\n";
718             }
719              
720             # faled
721             else {
722 0 0         $self->{debug} && print "put_file: failed for [$container/$object]\n";
723             }
724 0           return $status;
725             }
726              
727             =head2 get_fhstream("somecontainer","someobject",*FILE)
728              
729             This method takes a container, object and open file handle as arguments.
730             It retrieves the object in chunks, which it writes to *FILE as they are received.
731              
732             =cut
733              
734             sub get_fhstream() {
735 0     0 1   my $self = shift;
736 0           my $container = shift;
737 0           my $object = shift;
738 0           local (*OUT) = shift;
739 0           my $status = undef;
740              
741             # ensure we have enough information to continue
742 0 0 0       return undef unless ($self->{connected} && $container && $object && *OUT);
      0        
      0        
743              
744             # make sure the file handle we were passed is open
745 0 0         return undef unless ( (O_WRONLY | O_RDWR) & fcntl (OUT, F_GETFL, my $slush));
746              
747             # prepare the LWP request
748 0           my $ua = LWP::UserAgent->new;
749 0           my $req = HTTP::Request->new(GET => $self->{storage_url}.'/'.$container.'/'.$object);
750 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
751              
752             # create our custom handler for reading
753             my $res = $ua->request($req,
754             sub {
755 0     0     my ($chunk,$res) = @_;
756 0           print OUT $chunk;
757             }
758 0           );
759              
760             # success
761 0 0         if ($res->is_success) {
762 0           $status = 1;
763              
764 0 0         $self->{debug} && print "get_fhstream: success for [$container/$object]\n";
765             }
766              
767             # failed
768             else {
769 0 0         $self->{debug} && print "get_fhstream: failed for [$container/$object]\n";
770             }
771 0           return $status;
772             }
773              
774             =head2 rm("somecontainer","someobject")
775              
776             This method removes an object.
777              
778             =cut
779              
780             sub rm() {
781 0     0 1   my $self = shift;
782 0           my $container = shift;
783 0           my $object = shift;
784 0           my $status = undef;
785              
786             # ensure we have enough information to continue
787 0 0 0       return undef unless ($self->{connected} && $container && $object);
      0        
788              
789             # prepare the LWP object
790 0           my $ua = LWP::UserAgent->new;
791 0           my $req = HTTP::Request->new(DELETE => $self->{storage_url}.'/'.$container.'/'.$object);
792 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
793 0           $req->header( 'Content-Length' => '0' );
794              
795             # dispatch the request
796 0           my $res = $ua->request($req);
797              
798             # success
799 0 0         if ($res->is_success) {
800 0           $status = 1;
801 0 0         $self->{debug} && print "rm: success for [$container/$object]\n";
802             }
803              
804             # failed
805             else {
806 0 0         $self->{debug} && print "rm: failed for [$container/$object]\n";
807             }
808 0           return $status;
809             }
810              
811             =head2 create_manifest("somecontainer","someobject")
812              
813             This method creates a manifest for large-object support
814              
815             =cut
816              
817             sub create_manifest() {
818 0     0 1   my $self = shift;
819 0           my $container = shift;
820 0           my $object = shift;
821 0           my $status = undef;
822 0           my $content_type = 'application/octet-stream';
823 0           my $data;
824              
825             # ensure we have enough information to continue
826 0 0 0       return undef unless ($self->{connected} && $container && $object);
      0        
827              
828             # prepare the LWP request
829 0           my $ua = LWP::UserAgent->new;
830 0           my $req = HTTP::Request->new(PUT => $self->{storage_url}.'/'.$container.'/'.$object);
831 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
832 0           $req->header( 'Content-type' => $content_type);
833              
834             # point the manifest header to ourselves
835             # segments will be further along our path:
836             # somecontainer/manifest <- manifest
837             # somecontainer/manifest/segment1 <- segment
838             # somecontainer/manifest/segment2 <- segment
839 0           $req->header( 'X-Object-Manifest' => $container.'/'.$object);
840 0           $req->header( 'Content-Length' => '0' );
841 0           $req->content('');
842              
843             # dispatch request
844 0           my $res = $ua->request($req);
845              
846             # success
847 0 0         if ($res->is_success) {
848 0 0         $self->{debug} && print "create_manifest: success for [$container/$object]\n";
849             }
850              
851             # failed
852             else {
853 0 0         $self->{debug} && print "create_manifest: failed for [$container/$object]\n";
854             }
855 0           return $status;
856             }
857              
858             =head2 chmod("somecontainer","public")
859              
860             This method makes a container "public" or "private".
861              
862             =cut
863              
864             sub chmod() {
865 0     0 1   my $self = shift;
866 0           my $container = shift;
867 0           my $mode = shift;
868 0           my $status = undef;
869              
870             # ensure we have enough information to continue
871 0 0 0       return undef unless ($self->{connected} && $container && $mode);
      0        
872              
873             # fix-up mode
874 0 0         if ($mode eq 'public') {
875 0           $mode = '.r:*';
876             }
877             else {
878 0           $mode = '.r:-*';
879             }
880              
881             # prepare LWP request
882 0           my $ua = LWP::UserAgent->new;
883 0           my $req = HTTP::Request->new(POST => $self->{storage_url}.'/'.$container);
884 0           $req->header( 'X-STORAGE-TOKEN' => $self->{storage_token} );
885 0           $req->header( 'X-CONTAINER-READ' => $mode );
886              
887             # dispatch request
888 0           my $res = $ua->request($req);
889              
890             # success
891 0 0         if ($res->is_success) {
892 0           $status = 1;
893 0 0         $self->{debug} && print "chmod: success [$container]\n";
894             }
895              
896             # failed
897             else {
898 0 0         $self->{debug} && print "chmod: failed [$container]\n";
899             }
900 0           return $status;
901             }
902              
903             =head2 cdn("somecontainer","enable","true",300)
904              
905             This method manages a container's cdn configuration.
906              
907             Called with no arguments, it returns array reference, containing
908             all cdn-enabled containers.
909              
910             Called with just a container name, it returns a hash reference,
911             containing the cdn metadata for a container.
912              
913             Called with a container name and "disable", it disables the cdn
914             extensions for that container.
915              
916             Called with a container name, "enable", logging preference and TTL,
917             it configures cdn extensions for a container.
918              
919             =cut
920              
921             sub cdn() {
922 0     0 1   my $self = shift;
923 0           my $container = shift;
924 0           my $mode = shift;
925 0           my $logs = shift;
926 0           my $ttl = shift;
927              
928 0           my $status = undef;
929              
930             # ensure we have enough information to continue
931 0 0         return undef unless ($self->{connected});
932              
933             # prepare LWP request
934 0           my $ua = LWP::UserAgent->new;
935              
936             # handle bare call - list cdn-enabled containers
937 0 0         if ( ! $container ) {
938 0           my $req = HTTP::Request->new(GET => $self->{cdn_url});
939 0           $req->header( 'X-AUTH-TOKEN' => $self->{storage_token} );
940              
941             # dispatch request
942 0           my $res = $ua->request($req);
943              
944             # stuff return values into our result set
945 0 0         if ($res->is_success) {
946 0           my $list = [];
947 0           my @raw = split("\n",$res->content);
948 0           foreach (@raw) {
949 0 0         next if /^$/;
950 0           push @$list, $_;
951             }
952              
953 0 0         $self->{debug} && print "cdn: success\n";
954 0           return $list;
955             }
956             else {
957 0 0         $self->{debug} && print "cdn: failed\n";
958 0           return [];
959             }
960             }
961              
962             # get cdn attributes for a container
963 0 0 0       if ( $container && !$mode) {
964 0           my $req = HTTP::Request->new(HEAD => $self->{cdn_url}.'/'.$container);
965 0           $req->header( 'X-AUTH-TOKEN' => $self->{storage_token} );
966              
967             # dispatch request
968 0           my $res = $ua->request($req);
969              
970             # stuff return values into our result set
971 0 0         if ($res->is_success) {
972 0           my $metadata;
973 0           $metadata->{ttl} = $res->header("x-ttl");
974 0           $metadata->{logs} = $res->header("x-log-retention");
975 0           $metadata->{enabled} = $res->header("x-cdn-enabled");
976 0           $metadata->{uri} = $res->header("x-cdn-uri");
977 0           $metadata->{ssluri} = $res->header("x-cdn-uri");
978 0           $metadata->{ssluri} =~ s/http/https/g;
979              
980 0 0         $self->{debug} && print "cdn: success [$container]\n";
981 0           return $metadata;
982             }
983             else {
984 0 0         $self->{debug} && print "cdn: failed [$container]\n";
985 0           return undef;
986             }
987              
988             }
989              
990             # disable cdn support for a container
991 0 0 0       if ( $container && ( $mode eq 'disable')) {
992 0           my $req = HTTP::Request->new(POST => $self->{cdn_url}.'/'.$container);
993 0           $req->header( 'X-AUTH-TOKEN' => $self->{storage_token} );
994 0           $req->header( 'X-CDN-ENABLED' => 'False' );
995              
996             # dispatch request
997 0           my $res = $ua->request($req);
998              
999             # stuff return values into our result set
1000 0 0         if ($res->is_success) {
1001 0 0         $self->{debug} && print "cdn: success [$container] disable\n";
1002 0           return 1;
1003             }
1004             else {
1005 0 0         $self->{debug} && print "cdn: failed [$container] disable\n";
1006 0           return undef;
1007             }
1008             }
1009              
1010             # enable cdn on a container / update attributes
1011 0 0 0       if ( $container && ( $mode eq 'enable')) {
1012              
1013 0           my $req = HTTP::Request->new(POST => $self->{cdn_url}.'/'.$container);
1014 0           $req->header( 'X-AUTH-TOKEN' => $self->{storage_token} );
1015 0           $req->header( 'X-CDN-ENABLED' => 'True' );
1016 0 0         $logs && $req->header( 'X-LOG-RETENTION' => $logs );
1017 0 0         $ttl && $req->header( 'X-TTL' => $ttl );
1018              
1019             # dispatch request
1020 0           my $res = $ua->request($req);
1021              
1022             # stuff return values into our result set
1023 0 0         if ($res->is_success) {
1024 0           my $metadata;
1025 0           $metadata->{uri} = $res->header("x-cdn-uri");
1026 0           $metadata->{ssluri} = $res->header("x-cdn-uri");
1027 0           $metadata->{ssluri} =~ s/http/https/g;
1028              
1029 0 0         $self->{debug} && print "cdn: success [$container] enable\n";
1030 0           return $metadata;
1031             }
1032             else {
1033             # container might not exist - try again with a PUT
1034 0           $req = HTTP::Request->new(PUT => $self->{cdn_url}.'/'.$container);
1035 0           $req->header( 'X-AUTH-TOKEN' => $self->{storage_token} );
1036 0           $req->header( 'CONTENT-LENGTH' => 0 );
1037 0           $req->header( 'X-CDN-ENABLED' => 'True' );
1038 0 0         $logs && $req->header( 'X-LOG-RETENTION' => $logs );
1039 0 0         $ttl && $req->header( 'X-TTL' => $ttl );
1040              
1041             # dispatch request
1042 0           my $res = $ua->request($req);
1043              
1044 0 0         if ($res->is_success) {
1045 0           my $metadata;
1046 0           $metadata->{uri} = $res->header("x-cdn-uri");
1047 0           $metadata->{ssluri} = $res->header("x-cdn-uri");
1048 0           $metadata->{ssluri} =~ s/http/https/g;
1049              
1050 0 0         $self->{debug} && print "cdn: success container [$container] logs [$logs] ttl [$ttl] enable\n";
1051 0           return $metadata;
1052             }
1053             else {
1054 0 0         $self->{debug} && print "cdn: failed [$container] logs [$logs] ttl [$ttl] enable\n";
1055 0           return undef;
1056             }
1057             }
1058             }
1059             }
1060              
1061             1;
1062             __END__