File Coverage

blib/lib/Net/CloudStack.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package Net::CloudStack;
2            
3 1     1   27452 use 5.006;
  1         4  
  1         46  
4            
5 1     1   1792 use Mouse;
  1         38085  
  1         6  
6 1     1   410 use Mouse::Util::TypeConstraints;
  1         8  
  1         4  
7 1     1   1023 use Digest::SHA qw(hmac_sha1);
  1         4847  
  1         103  
8 1     1   1020 use MIME::Base64;
  1         1100  
  1         65  
9 1     1   1084 use LWP::UserAgent;
  1         51037  
  1         33  
10 1     1   1161 use Encode;
  1         18681  
  1         100  
11 1     1   444 use XML::Twig;
  0            
  0            
12             use URI::Encode;
13             use JSON;
14             use Carp;
15             use Data::Dumper;
16            
17             subtype 'CloudStack::YN'
18             => as 'Str'
19             => where { $_ =~ /^(yes|no)$/ }
20             => message { "Please input yes or no" }
21             ;
22            
23             has 'base_url' => ( #http://localhost:8080
24             is => 'rw',
25             isa => 'Str',
26             required => 1,
27             );
28            
29             has 'api_path' => ( #/client/api?
30             is => 'rw',
31             isa => 'Str',
32             required => 1,
33             );
34            
35             has 'api_key' => (
36             is => 'rw',
37             isa => 'Str',
38             required => 1,
39             );
40            
41             has 'secret_key' => (
42             is => 'rw',
43             isa => 'Str',
44             required => 1,
45             );
46            
47             has 'send_request' => (
48             is => 'rw',
49             isa => 'CloudStack::YN',
50             default => 'no',
51             );
52            
53             has 'xml_json' => (
54             is => 'rw',
55             isa => 'Str',
56             default => 'xml',
57             );
58            
59             has 'url' => (
60             is => 'rw',
61             isa => 'Str',
62             );
63            
64             has 'response' => (
65             is => 'rw',
66             isa => 'Str',
67             );
68            
69             __PACKAGE__->meta->make_immutable;
70             no Mouse;
71             no Mouse::Util::TypeConstraints;
72            
73             ### FOR TEST ###
74            
75             sub test{
76             my ($self) = @_;
77             my @required = ();
78            
79             print "BASE URL:".$self->base_url."\n";
80             print "API PATH:".$self->api_path."\n";
81             print "API KEY:".$self->api_key."\n";
82             print "SECRET KEY:".$self->secret_key."\n";
83             print "SEND_REQUEST:".$self->send_request."\n";
84             print "XML_JSON:".$self->xml_json."\n";
85             }
86            
87            
88             ### SUB ROUTINE ###
89            
90             ### COMMAND ###
91             sub proc{
92             my ($self, $cmd, $opt, $required) = @_;
93            
94             if(!defined($opt)){
95             $opt = "";
96             }
97             else{
98             $opt =~ s/^(.+\=\=)\s+\S*?(\&.+)$/$1$2/; # for SSH Public Key
99             $opt =~ s/^(.+\=\=)\s+\S+$/$1/; # for SSH Public Key
100            
101             $opt =~ s/([\=\&])\s+/$1/g;
102             $opt =~ s/\s+([\=\&])/$1/g;
103             }
104            
105             $cmd =~ s/.*:://;
106            
107             foreach (@$required){
108             croak "$_ is required" if(!defined($opt) || $opt !~ /[\s\&]*$_\s*\=/);
109             }
110            
111             $self->gen_url($cmd, $opt);
112             if($self->send_request =~ /yes/i){
113             $self->gen_response;
114             }
115             }
116            
117            
118             sub gen_url{
119             my ($self, $cmd, $opt) = @_;
120             my $base_url = $self->base_url;
121             my $api_path = $self->api_path;
122             my $api_key = $self->api_key;
123             my $secret_key = $self->secret_key;
124             my $xml_json = $self->xml_json;
125             my $uri = URI::Encode->new();
126            
127             #step1
128             if($opt){
129             $cmd .= "&".$opt;
130             }
131             if($xml_json =~ /json/i){
132             $cmd .= "&response=json";
133             }
134             my $query = "command=".$cmd."&apiKey=".$api_key;
135             my @list = split(/&/,$query);
136             foreach (@list){
137             if(/(\w+(?:\[\d+\]\.\w+)?)\=(\w.+)/){
138             my $field = $1;
139             my $value = $uri->encode($2, 1); # encode_reserved option is set to 1
140             $_ = $field."=".$value;
141             }
142             }
143             my $output_tmp = join("&",sort @list);
144            
145             #step2
146             foreach (@list){
147             $_ = lc($_);
148             }
149             my $output = join("&",sort @list);
150            
151             #step3
152             my $digest = hmac_sha1($output, $secret_key);
153             my $base64_encoded = encode_base64($digest);chomp($base64_encoded);
154             my $url_encoded = $uri->encode($base64_encoded, 1); # encode_reserved option is set to 1
155             my $url = $base_url."/".$api_path.$output_tmp."&signature=".$url_encoded;
156             $self->url("$url");
157             # print Dumper($url);
158             }
159            
160             sub gen_response{
161             my ($self) = shift;
162             my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
163             my $ua_res = $ua->get($self->url);
164            
165             # print Dumper($ua_res);
166             if($ua_res->{_content} =~ /^provider_error\:/){
167             $self->response($ua_res->{_content}."\n");
168             }
169            
170             else{
171             #json
172             if($self->xml_json =~ /json/i){
173             my $obj = from_json(encode('utf8',$ua_res->decoded_content));
174             my $json = JSON->new->pretty(1)->encode($obj);
175             $self->response("$json");
176             }
177            
178             #xml
179             else{
180             my $parser = XML::Simple->new;
181            
182             my $xml = encode('utf8',$ua_res->decoded_content);#Please Change cp932 for Win.
183             my $twig = XML::Twig->new(pretty_print => 'indented', );
184             $twig->parse($xml);
185            
186             my $response = $twig->sprint;
187             $self->response("$response");
188             }
189             }
190             }
191            
192            
193             =head1 NAME
194            
195             Net::CloudStack - Bindings for the CloudStack API
196            
197             =head1 VERSION
198            
199             Version 0.01005
200            
201             =cut
202            
203             our $VERSION = '0.01005';
204            
205            
206             =head1 SYNOPSIS
207            
208             use Net::CloudStack;
209             my $api = Net::CloudStack->new(
210             base_url => 'http://...',
211             api_path => 'client/api?',
212             api_key => '',
213             secret_key => '',
214             xml_json => 'json', #response format.you can select json or xml. xml is default.
215             send_request => 'yes', #yes or no.
216             #When you select yes,you can get response.
217             #If you don't want to get response(only generating url),please input no.
218             );
219            
220             # CloudStack API Methods
221             $api->proc($cmd, $opt);
222             $api->proc("listVirtualMachines");
223             $api->proc("listVirtualMachines","id=123");
224            
225             $api->proc("deployVirtualMachine","serviceofferingid=1&templateid=1&zoneid=1"); # some IDs are depend on your environment.
226            
227             # Original Methods
228             print $api->url; # print generated url
229             print $api->response; # print API response
230            
231             =head1 METHODS
232            
233             This module supports all CloudStack commands,basically you can use methods as following,
234            
235             $api->some_command("parm1=$parm1&parm2=$parm2")
236            
237             Please refer B in following B.
238            
239             L
240            
241             Followings are some examples for API command,
242            
243             =head2 listVirtualMachines
244            
245             $api->proc("listVirtualMachines")
246             $api->proc("listVirtualMachines","id=$id")
247            
248             =head2 deployVirtualMachine
249            
250             $api->proc("deployVirtualMachine","serviceoffeingid=$serviceoffeingid&templateid=$templateid&zoneid=$zoneid")
251            
252             =head2 startVirtualMachine/stopVirtualMachine
253            
254             $api->proc("startVirtualMachine","id=$id")
255             $api->proc("stopVirtualMachine","id=$id")
256            
257             Followings are some examples for original command,
258            
259             =head2 test
260            
261             $api->test()
262            
263             This method prints each defined attributes(base_url,api_path,api_key,secret_key,send_request,xml_json).
264            
265             =head2 url
266            
267             $api->url()
268            
269             This method prints generated URL that is send to CloudStack API.
270            
271             =head2 test
272            
273             $api->response()
274            
275             This method prints response from CloudStack API.
276            
277             =head1 AUTHOR
278            
279             Shugo Numano, C<< >>
280            
281             @shugonumano
282            
283             =head1 BUGS
284            
285             Please report any bugs or feature requests to C, or through
286             the web interface at L. I will be notified, and then you'll
287             automatically be notified of progress on your bug as I make changes.
288            
289             =head1 SUPPORT
290            
291             You can find documentation for this module with the perldoc command.
292            
293             perldoc Net::CloudStack
294            
295            
296             You can also look for information at:
297            
298             =over 5
299            
300             =item * Developer's Guide:CloudStack
301            
302             L
303            
304             =item * RT: CPAN's request tracker (report bugs here)
305            
306             L
307            
308             =item * AnnoCPAN: Annotated CPAN documentation
309            
310             L
311            
312             =item * CPAN Ratings
313            
314             L
315            
316             =item * Search CPAN
317            
318             L
319            
320             =back
321            
322            
323             =head1 ACKNOWLEDGEMENTS
324            
325            
326             =head1 LICENSE AND COPYRIGHT
327            
328             Copyright 2011 Shugo Numano.
329            
330             This program is free software; you can redistribute it and/or modify it
331             under the terms of either: the GNU General Public License as published
332             by the Free Software Foundation; or the Artistic License.
333            
334             See http://dev.perl.org/licenses/ for more information.
335            
336            
337             =cut
338            
339             1; # End of Net::CloudStack