File Coverage

blib/lib/Net/Amazon/EC2.pm
Criterion Covered Total %
statement 283 1593 17.7
branch 16 634 2.5
condition 4 51 7.8
subroutine 77 140 55.0
pod 58 61 95.0
total 438 2479 17.6


line stmt bran cond sub pod time code
1             package Net::Amazon::EC2;
2 1     1   28516 use Moose;
  1         526678  
  1         7  
3              
4 1     1   8090 use strict;
  1         3  
  1         39  
5 1     1   6 use vars qw($VERSION);
  1         3  
  1         52  
6              
7 1     1   1666 use XML::Simple;
  1         12377  
  1         9  
8 1     1   1493 use LWP::UserAgent;
  1         55059  
  1         37  
9 1     1   859 use LWP::Protocol::https;
  1         113843  
  1         48  
10 1     1   1007 use Digest::SHA qw(hmac_sha256 hmac_sha256_hex sha256_hex);
  1         2966  
  1         90  
11 1     1   8 use URI;
  1         2  
  1         28  
12 1     1   864 use MIME::Base64 qw(encode_base64 decode_base64);
  1         650  
  1         68  
13 1     1   17 use POSIX qw(strftime);
  1         2  
  1         10  
14 1     1   904 use Params::Validate qw(validate SCALAR ARRAYREF HASHREF);
  1         3406  
  1         84  
15 1     1   1004 use Data::Dumper qw(Dumper);
  1         6470  
  1         71  
16 1     1   8 use URI::Escape qw(uri_escape_utf8);
  1         3  
  1         53  
17 1     1   1001 use Encode qw(encode_utf8);
  1         10819  
  1         100  
18 1     1   7 use Carp;
  1         1  
  1         55  
19              
20 1     1   567 use Net::Amazon::EC2::DescribeImagesResponse;
  1         4  
  1         50  
21 1     1   776 use Net::Amazon::EC2::DescribeKeyPairsResponse;
  1         3  
  1         36  
22 1     1   719 use Net::Amazon::EC2::DescribeSubnetResponse;
  1         4  
  1         40  
23 1     1   739 use Net::Amazon::EC2::GroupSet;
  1         3  
  1         35  
24 1     1   686 use Net::Amazon::EC2::InstanceState;
  1         3  
  1         35  
25 1     1   667 use Net::Amazon::EC2::IpPermission;
  1         5  
  1         48  
26 1     1   881 use Net::Amazon::EC2::LaunchPermission;
  1         3  
  1         47  
27 1     1   708 use Net::Amazon::EC2::LaunchPermissionOperation;
  1         4  
  1         39  
28 1     1   809 use Net::Amazon::EC2::ProductCode;
  1         4  
  1         42  
29 1     1   816 use Net::Amazon::EC2::ProductInstanceResponse;
  1         3  
  1         41  
30 1     1   780 use Net::Amazon::EC2::ReservationInfo;
  1         4  
  1         46  
31 1     1   877 use Net::Amazon::EC2::RunningInstances;
  1         3  
  1         45  
32 1     1   763 use Net::Amazon::EC2::SecurityGroup;
  1         5  
  1         41  
33 1     1   750 use Net::Amazon::EC2::UserData;
  1         4  
  1         34  
34 1     1   651 use Net::Amazon::EC2::UserIdGroupPair;
  1         3  
  1         32  
35 1     1   617 use Net::Amazon::EC2::IpRange;
  1         3  
  1         31  
36 1     1   683 use Net::Amazon::EC2::KeyPair;
  1         3  
  1         35  
37 1     1   658 use Net::Amazon::EC2::DescribeImageAttribute;
  1         5  
  1         57  
38 1     1   769 use Net::Amazon::EC2::ConsoleOutput;
  1         4  
  1         46  
39 1     1   726 use Net::Amazon::EC2::Errors;
  1         4  
  1         36  
40 1     1   731 use Net::Amazon::EC2::Error;
  1         5  
  1         39  
41 1     1   723 use Net::Amazon::EC2::ConfirmProductInstanceResponse;
  1         3  
  1         40  
42 1     1   720 use Net::Amazon::EC2::DescribeAddress;
  1         2  
  1         34  
43 1     1   671 use Net::Amazon::EC2::AvailabilityZone;
  1         3  
  1         40  
44 1     1   745 use Net::Amazon::EC2::BlockDeviceMapping;
  1         4  
  1         42  
45 1     1   751 use Net::Amazon::EC2::PlacementResponse;
  1         3  
  1         32  
46 1     1   636 use Net::Amazon::EC2::Volume;
  1         4  
  1         46  
47 1     1   758 use Net::Amazon::EC2::Attachment;
  1         5  
  1         42  
48 1     1   741 use Net::Amazon::EC2::Snapshot;
  1         4  
  1         44  
49 1     1   838 use Net::Amazon::EC2::BundleInstanceResponse;
  1         4  
  1         44  
50 1     1   759 use Net::Amazon::EC2::Region;
  1         4  
  1         39  
51 1     1   754 use Net::Amazon::EC2::ReservedInstance;
  1         4  
  1         42  
52 1     1   754 use Net::Amazon::EC2::ReservedInstanceOffering;
  1         3  
  1         43  
53 1     1   791 use Net::Amazon::EC2::MonitoredInstance;
  1         3  
  1         38  
54 1     1   693 use Net::Amazon::EC2::InstancePassword;
  1         3  
  1         41  
55 1     1   778 use Net::Amazon::EC2::SnapshotAttribute;
  1         5  
  1         43  
56 1     1   760 use Net::Amazon::EC2::CreateVolumePermission;
  1         5  
  1         43  
57 1     1   805 use Net::Amazon::EC2::AvailabilityZoneMessage;
  1         4  
  1         36  
58 1     1   650 use Net::Amazon::EC2::StateReason;
  1         4  
  1         39  
59 1     1   702 use Net::Amazon::EC2::InstanceBlockDeviceMapping;
  1         3  
  1         40  
60 1     1   681 use Net::Amazon::EC2::InstanceStateChange;
  1         4  
  1         39  
61 1     1   774 use Net::Amazon::EC2::DescribeInstanceAttributeResponse;
  1         4  
  1         43  
62 1     1   753 use Net::Amazon::EC2::EbsInstanceBlockDeviceMapping;
  1         3  
  1         38  
63 1     1   674 use Net::Amazon::EC2::EbsBlockDevice;
  1         4  
  1         34  
64 1     1   673 use Net::Amazon::EC2::TagSet;
  1         3  
  1         34  
65 1     1   705 use Net::Amazon::EC2::DescribeTags;
  1         3  
  1         36  
66 1     1   681 use Net::Amazon::EC2::Details;
  1         5  
  1         35  
67 1     1   648 use Net::Amazon::EC2::Events;
  1         4  
  1         37  
68 1     1   679 use Net::Amazon::EC2::InstanceStatus;
  1         4  
  1         34  
69 1     1   669 use Net::Amazon::EC2::InstanceStatuses;
  1         3  
  1         38  
70 1     1   696 use Net::Amazon::EC2::SystemStatus;
  1         4  
  1         32  
71 1     1   679 use Net::Amazon::EC2::NetworkInterfaceSet;
  1         5  
  1         19404  
72              
73             $VERSION = '0.31';
74              
75             =head1 NAME
76              
77             Net::Amazon::EC2 - Perl interface to the Amazon Elastic Compute Cloud (EC2)
78             environment.
79              
80             =head1 VERSION
81              
82             This is Net::Amazon::EC2 version 0.31
83              
84             EC2 Query API version: '2014-06-15'
85              
86             =head1 SYNOPSIS
87              
88             use Net::Amazon::EC2;
89              
90             my $ec2 = Net::Amazon::EC2->new(
91             AWSAccessKeyId => 'PUBLIC_KEY_HERE',
92             SecretAccessKey => 'SECRET_KEY_HERE',
93             signature_version => 4,
94             );
95              
96             # Start 1 new instance from AMI: ami-XXXXXXXX
97             my $instance = $ec2->run_instances(ImageId => 'ami-XXXXXXXX', MinCount => 1, MaxCount => 1);
98              
99             my $running_instances = $ec2->describe_instances;
100              
101             foreach my $reservation (@$running_instances) {
102             foreach my $instance ($reservation->instances_set) {
103             print $instance->instance_id . "\n";
104             }
105             }
106              
107             my $instance_id = $instance->instances_set->[0]->instance_id;
108              
109             print "$instance_id\n";
110              
111             # Terminate instance
112              
113             my $result = $ec2->terminate_instances(InstanceId => $instance_id);
114              
115             If an error occurs while communicating with EC2, these methods will
116             throw a L<Net::Amazon::EC2::Errors> exception.
117              
118             =head1 DESCRIPTION
119              
120             This module is a Perl interface to Amazon's Elastic Compute Cloud. It uses the Query API to
121             communicate with Amazon's Web Services framework.
122              
123             =head1 CLASS METHODS
124              
125             =head2 new(%params)
126              
127             This is the constructor, it will return you a Net::Amazon::EC2 object to work with. It takes
128             these parameters:
129              
130             =over
131              
132             =item AWSAccessKeyId (required, unless an IAM role is present)
133              
134             Your AWS access key. For information on IAM roles, see L<http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UsingIAM.html#UsingIAMrolesWithAmazonEC2Instances>
135              
136             =item SecretAccessKey (required, unless an IAM role is present)
137              
138             Your secret key, B<WARNING!> don't give this out or someone will be able to use your account
139             and incur charges on your behalf.
140              
141             =item SecurityToken (optional)
142              
143             When using temporary credentials from STS the Security Token must be passed
144             in along with the temporary AWSAccessKeyId and SecretAccessKey. The most common case is when using IAM credentials with the addition of MFA (multi-factor authentication). See L<http://docs.aws.amazon.com/STS/latest/UsingSTS/Welcome.html>
145              
146             =item region (optional)
147              
148             The region to run the API requests through. Defaults to us-east-1.
149              
150             =item ssl (optional)
151              
152             If set to a true value, the base_url will use https:// instead of http://. Setting base_url
153             explicitly will override this. Defaults to true as of 0.22.
154              
155             =item debug (optional)
156              
157             A flag to turn on debugging. Among other useful things, it will make the failing api calls print
158             a stack trace. It is turned off by default.
159              
160             =item return_errors (optional)
161              
162             Previously, Net::Amazon::EC2 would return a L<Net::Amazon::EC2::Errors>
163             object when it encountered an error condition. As of 0.19, this
164             object is thrown as an exception using croak or confess depending on
165             if the debug flag is set.
166              
167             If you want/need the old behavior, set this attribute to a true value.
168              
169             =back
170              
171             =cut
172              
173             has 'AWSAccessKeyId' => (
174             is => 'ro',
175             isa => 'Str',
176             required => 1,
177             lazy => 1,
178             default => sub {
179             if (defined($_[0]->temp_creds)) {
180             return $_[0]->temp_creds->{'AccessKeyId'};
181             } else {
182             return undef;
183             }
184             }
185             );
186             has 'SecretAccessKey' => (
187             is => 'ro',
188             isa => 'Str',
189             required => 1,
190             lazy => 1,
191             default => sub {
192             if (defined($_[0]->temp_creds)) {
193             return $_[0]->temp_creds->{'SecretAccessKey'};
194             } else {
195             return undef;
196             }
197             }
198             );
199             has 'SecurityToken' => (
200             is => 'ro',
201             isa => 'Str',
202             required => 0,
203             lazy => 1,
204             predicate => 'has_SecurityToken',
205             default => sub {
206             if (defined($_[0]->temp_creds)) {
207             return $_[0]->temp_creds->{'Token'};
208             } else {
209             return undef;
210             }
211             }
212             );
213             has 'debug' => ( is => 'ro', isa => 'Str', required => 0, default => 0 );
214             has 'signature_version' => ( is => 'ro', isa => 'Int', required => 1, default => 2 );
215             has 'version' => ( is => 'ro', isa => 'Str', required => 1, default => '2014-06-15' );
216             has 'region' => ( is => 'ro', isa => 'Str', required => 1, default => 'us-east-1' );
217             has 'ssl' => ( is => 'ro', isa => 'Bool', required => 1, default => 1 );
218             has 'return_errors' => ( is => 'ro', isa => 'Bool', default => 0 );
219             has 'base_url' => (
220             is => 'ro',
221             isa => 'Str',
222             required => 1,
223             lazy => 1,
224             default => sub {
225             return 'http' . ($_[0]->ssl ? 's' : '') . '://ec2.' . $_[0]->region . '.amazonaws.com';
226             }
227             );
228             has 'temp_creds' => (
229             is => 'ro',
230             lazy => 1,
231             default => sub {
232             my $ret;
233             $ret = $_[0]->_fetch_iam_security_credentials();
234             },
235             predicate => 'has_temp_creds'
236             );
237              
238             sub timestamp {
239 3     3 0 237 return strftime("%Y-%m-%dT%H:%M:%SZ",gmtime);
240             }
241              
242             sub _fetch_iam_security_credentials {
243 0     0   0 my $self = shift;
244 0         0 my $retval = {};
245              
246 0         0 my $ua = LWP::UserAgent->new();
247             # Fail quickly if this is not running on an EC2 instance
248 0         0 $ua->timeout(2);
249              
250 0         0 my $url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';
251 0         0 $self->_debug("Attempting to fetch instance credentials");
252              
253 0         0 my $res = $ua->get($url);
254 0 0       0 if ($res->code == 200) {
255             # Assumes the first profile is the only profile
256 0         0 my $profile = (split /\n/, $res->content())[0];
257              
258 0         0 $res = $ua->get($url . $profile);
259              
260 0 0       0 if ($res->code == 200) {
261 0         0 $retval->{'Profile'} = $profile;
262 0         0 foreach (split /\n/, $res->content()) {
263 0 0 0     0 return undef if /Code/ && !/Success/;
264 0 0       0 if (m/.*"([^"]+)"\s+:\s+"([^"]+)",/) {
265 0         0 $retval->{$1} = $2;
266             }
267             }
268 0 0       0 return $retval if (keys %{$retval});
  0         0  
269             }
270             }
271 0         0 return undef;
272             }
273              
274             sub _sign {
275 3     3   9 my $self = shift;
276              
277 3 50       121 if ( $self->signature_version == 2 ) {
    0          
278 3         81 $self->_sign_v2(@_);
279             }
280             elsif ( $self->signature_version == 4 ) {
281 0         0 $self->_sign_v4(@_);
282             }
283             else {
284 0         0 die "I don't know what signature version " .
285             $self->signature_version . "means.\n";
286             }
287             }
288              
289             sub _sign_v2 {
290 3     3   7 my $self = shift;
291 3         13 my %args = @_;
292 3         9 my $action = delete $args{Action};
293 3         8 my %sign_hash = %args;
294 3         16 my $timestamp = $self->timestamp;
295              
296 3         124 $sign_hash{AWSAccessKeyId} = $self->AWSAccessKeyId;
297 3         9 $sign_hash{Action} = $action;
298 3         13 $sign_hash{Timestamp} = $timestamp;
299 3         105 $sign_hash{Version} = $self->version;
300 3         101 $sign_hash{SignatureVersion} = $self->signature_version;
301 3         8 $sign_hash{SignatureMethod} = "HmacSHA256";
302 3 50 33     127 if ($self->has_temp_creds || $self->has_SecurityToken) {
303 0         0 $sign_hash{SecurityToken} = $self->SecurityToken;
304             }
305              
306 3         10 my $sign_this = "POST\n";
307 3         99 my $uri = URI->new($self->base_url);
308              
309 3         8549 $sign_this .= lc($uri->host) . "\n";
310 3         216 $sign_this .= "/\n";
311              
312 3         6 my @signing_elements;
313              
314 3         88 foreach my $key (sort keys %sign_hash) {
315 18         451 push @signing_elements, uri_escape_utf8($key)."=".uri_escape_utf8($sign_hash{$key});
316             }
317              
318 3         77 $sign_this .= join "&", @signing_elements;
319              
320 3         18 $self->_debug("QUERY TO SIGN: $sign_this");
321 3         120 my $encoded = $self->_hashit($self->SecretAccessKey, $sign_this);
322              
323 3         13 my $content = join "&", @signing_elements, 'Signature=' . uri_escape_utf8($encoded);
324              
325 3         83 my $ur = $uri->as_string();
326 3         26 $self->_debug("GENERATED QUERY URL: $ur");
327 3         38 my $ua = LWP::UserAgent->new();
328 3         3858 $ua->env_proxy;
329 3         5010 my $res = $ua->post($ur, Content => $content);
330 3         17714 $self->_handle_response($res);
331             }
332              
333             sub _hashit {
334 3     3   7 my $self = shift;
335 3         8 my ($secret_access_key, $query_string) = @_;
336            
337 3         179 return encode_base64(hmac_sha256($query_string, $secret_access_key), '');
338             }
339              
340             sub _sign_v4 {
341 0     0   0 my $self = shift;
342 0         0 my %args = @_;
343 0         0 my $algorithm = 'AWS4-HMAC-SHA256';
344 0         0 my $service = 'ec2';
345 0         0 my @now = gmtime();
346 0         0 my $amz_date = strftime("%Y%m%dT%H%M%SZ", @now);
347 0         0 my $datestamp = strftime("%Y%m%d", @now);
348 0         0 my $credential_scope = $datestamp . "/" . $self->region . "/" . $service . "/aws4_request";
349 0         0 my $content_type = "application/x-www-form-urlencoded";
350 0         0 my $signed_headers = "content-type;host;x-amz-date";
351             # Assemble the content
352 0         0 $args{Version} = $self->version;
353 0 0       0 if ($self->has_temp_creds) {
354 0         0 $args{'X-Amz-Security-Token'} = $self->temp_creds->{'Token'};
355             }
356 0         0 my @content_elements;
357 0         0 foreach my $key (sort keys %args) {
358 0         0 push @content_elements, uri_escape_utf8($key)."=".uri_escape_utf8($args{$key});
359             }
360 0         0 my $content .= join "&", @content_elements;
361              
362 0         0 $self->_debug("CONTENT: $content");
363              
364             # Step 1: create canonical request string
365 0         0 my $uri = URI->new($self->base_url);
366 0         0 my $canonical_headers = "content-type:" . $content_type . "\n" .
367             "host:" . lc($uri->host) . "\n" .
368             "x-amz-date:" . $amz_date . "\n";
369              
370 0         0 my $canonical_request = "POST\n"; # method
371 0         0 $canonical_request .= "/\n"; # uri
372 0         0 $canonical_request .= "\n"; # query-string
373 0         0 $canonical_request .= $canonical_headers . "\n"; # headers
374 0         0 $canonical_request .= $signed_headers . "\n"; # signed headers
375 0         0 $canonical_request .= sha256_hex($content); # payload
376 0         0 $self->_debug("CANONICAL REQUEST: $canonical_request");
377              
378             # Step 2: create string to sign
379 0         0 my $sign_this = $algorithm . "\n";
380 0         0 $sign_this .= $amz_date . "\n";
381 0         0 $sign_this .= $credential_scope . "\n";
382 0         0 $sign_this .= sha256_hex($canonical_request);
383              
384 0         0 $self->_debug("STRING TO SIGN: $sign_this");
385              
386             # Step 3: calculate the signature
387 0         0 my $key_date = $self->_hmac(encode_utf8('AWS4' . $self->SecretAccessKey), $datestamp);
388 0         0 my $key_region = $self->_hmac($key_date, $self->region);
389 0         0 my $key_service = $self->_hmac($key_region, $service);
390 0         0 my $signing_key = $self->_hmac($key_service, 'aws4_request');
391              
392 0         0 my $signature = $self->_hmac($signing_key, encode_utf8($sign_this), 1);
393              
394             # send request
395 0         0 my $auth_header = $algorithm .
396             ' Credential=' . $self->AWSAccessKeyId . '/' . $credential_scope .
397             ', SignedHeaders=' . $signed_headers .
398             ', Signature=' . $signature;
399              
400              
401 0         0 my $req = HTTP::Request->new('POST', $uri,
402             [ "Authorization" => $auth_header,
403             "Content-Type" => $content_type,
404             "X-Amz-Date" => $amz_date ] ,
405             $content
406             );
407 0         0 $self->_debug("HTTP REQUEST: " . $req->as_string() . "\n");
408              
409 0         0 my $ua = LWP::UserAgent->new();
410 0         0 $ua->env_proxy;
411 0         0 my $res = $ua->request($req);
412 0         0 $self->_handle_response($res);
413             }
414              
415             sub _handle_response {
416 3     3   8 my ($self, $res) = @_;
417             # We should force <item> elements to be in an array
418 3         42 my $xs = XML::Simple->new(
419             ForceArray => qr/(?:item|Errors)/i, # Always want item elements unpacked to arrays
420             KeyAttr => '', # Turn off folding for 'id', 'name', 'key' elements
421             SuppressEmpty => undef, # Turn empty values into explicit undefs
422             );
423 3         401 my $xml;
424            
425             # Check the result for connectivity problems, if so throw an error
426 3 50       12 if ($res->code >= 500) {
427 3         45 my $message = $res->status_line;
428 3         33 $xml = <<EOXML;
429             <xml>
430             <RequestID>N/A</RequestID>
431             <Errors>
432             <Error>
433             <Code>HTTP POST FAILURE</Code>
434             <Message>$message</Message>
435             </Error>
436             </Errors>
437             </xml>
438             EOXML
439              
440             }
441             else {
442 0         0 $xml = $res->content();
443             }
444              
445 3         16 my $ref = $xs->XMLin($xml);
446 3 100       91128 warn Dumper($ref) . "\n\n" if $self->debug == 1;
447              
448 3         427 return $ref;
449             }
450              
451             sub _parse_errors {
452 3     3   7 my $self = shift;
453 3         5 my $errors_xml = shift;
454            
455 3         7 my $es;
456 3         9 my $request_id = $errors_xml->{RequestID};
457              
458 3         7 foreach my $e (@{$errors_xml->{Errors}}) {
  3         12  
459             my $error = Net::Amazon::EC2::Error->new(
460             code => $e->{Error}{Code},
461             message => $e->{Error}{Message},
462 3         145 );
463            
464 3         12 push @$es, $error;
465             }
466            
467 3         131 my $errors = Net::Amazon::EC2::Errors->new(
468             request_id => $request_id,
469             errors => $es,
470             );
471              
472 3         7 foreach my $error (@{$errors->errors}) {
  3         118  
473 3         111 $self->_debug("ERROR CODE: " . $error->code . " MESSAGE: " . $error->message . " FOR REQUEST: " . $errors->request_id);
474             }
475              
476             # User wants old behaviour
477 3 100       113 if ($self->return_errors) {
478 1         16 return $errors;
479             }
480              
481             # Print a stack trace if debugging is enabled
482 2 100       65 if ($self->debug) {
483 1         37 confess 'Last error was: ' . $es->[-1]->message;
484             } else {
485 1         23 croak $errors;
486             }
487             }
488              
489             sub _debug {
490 9     9   20 my $self = shift;
491 9         13 my $message = shift;
492            
493 9 50 66     323 if ((grep { defined && length} $self->debug) && $self->debug == 1) {
  9 100       342  
494 3         708 print "$message\n\n\n\n";
495             }
496             }
497              
498             sub _hmac {
499 0     0   0 my $self = shift;
500 0         0 my ($key, $msg, $hex) = @_;
501 0 0       0 my $func = $hex ? \&hmac_sha256_hex : \&hmac_sha256;
502 0         0 return &$func(encode_utf8($msg), $key);
503             }
504              
505             sub _build_filters {
506 3     3   9 my ($self, $args) = @_;
507 3         7 my $filters = delete $args->{Filter};
508              
509 3 50 33     15 return unless $filters && ref($filters) eq 'ARRAY';
510              
511 0 0       0 $filters = [ $filters ] unless ref($filters->[0]) eq 'ARRAY';
512 0         0 my $count = 1;
513 0         0 foreach my $filter (@{$filters}) {
  0         0  
514 0         0 my ($name, @args) = @$filter;
515 0         0 $args->{"Filter." . $count.".Name"} = $name;
516 0         0 $args->{"Filter." . $count.".Value.".$_} = $args[$_-1] for 1..scalar @args;
517 0         0 $count++;
518             }
519             }
520              
521             =head1 OBJECT METHODS
522              
523             =head2 allocate_address()
524              
525             Acquires an elastic IP address which can be associated with an EC2-classic instance to create a movable static IP. Takes no arguments.
526              
527             Returns the IP address obtained.
528              
529             =cut
530              
531             sub allocate_address {
532 0     0 1 0 my $self = shift;
533              
534 0         0 my $xml = $self->_sign(Action => 'AllocateAddress');
535              
536 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
537 0         0 return $self->_parse_errors($xml);
538             }
539             else {
540 0         0 return $xml->{publicIp};
541             }
542             }
543              
544             =head2 allocate_vpc_address()
545              
546             Acquires an elastic IP address which can be associated with a VPC instance to create a movable static IP. Takes no arguments.
547              
548             Returns the allocationId of the allocated address.
549              
550             =cut
551              
552             sub allocate_vpc_address {
553 0     0 1 0 my $self = shift;
554              
555 0         0 my $xml = $self->_sign(Action => 'AllocateAddress', Domain => 'vpc');
556              
557 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
558 0         0 return $self->_parse_errors($xml);
559             }
560             else {
561 0         0 return $xml->{allocationId};
562             }
563             }
564              
565             =head2 associate_address(%params)
566              
567             Associates an elastic IP address with an instance. It takes the following arguments:
568              
569             =over
570              
571             =item InstanceId (required)
572              
573             The instance id you wish to associate the IP address with
574              
575             =item PublicIp (optional)
576              
577             The IP address. Used for allocating addresses to EC2-classic instances.
578              
579             =item AllocationId (optional)
580              
581             The allocation ID. Used for allocating address to VPC instances.
582              
583             =back
584              
585             Returns true if the association succeeded.
586              
587             =cut
588              
589             sub associate_address {
590 0     0 1 0 my $self = shift;
591 0         0 my %args = validate( @_, {
592             InstanceId => { type => SCALAR },
593             PublicIp => { type => SCALAR, optional => 1 },
594             AllocationId => { type => SCALAR, optional => 1 },
595             });
596            
597 0         0 my $xml = $self->_sign(Action => 'AssociateAddress', %args);
598              
599 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
600 0         0 return $self->_parse_errors($xml);
601             }
602             else {
603 0 0       0 if ($xml->{return} eq 'true') {
604 0         0 return 1;
605             }
606             else {
607 0         0 return undef;
608             }
609             }
610             }
611              
612             =head2 attach_volume(%params)
613              
614             Attach a volume to an instance.
615              
616             =over
617              
618             =item VolumeId (required)
619              
620             The volume id you wish to attach.
621              
622             =item InstanceId (required)
623              
624             The instance id you wish to attach the volume to.
625              
626             =item Device (required)
627              
628             The device id you want the volume attached as.
629              
630             =back
631              
632             Returns a Net::Amazon::EC2::Attachment object containing the resulting volume status.
633              
634             =cut
635              
636             sub attach_volume {
637 0     0 1 0 my $self = shift;
638 0         0 my %args = validate( @_, {
639             VolumeId => { type => SCALAR },
640             InstanceId => { type => SCALAR },
641             Device => { type => SCALAR },
642             });
643              
644 0         0 my $xml = $self->_sign(Action => 'AttachVolume', %args);
645            
646 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
647 0         0 return $self->_parse_errors($xml);
648             }
649             else {
650             my $attachment = Net::Amazon::EC2::Attachment->new(
651             volume_id => $xml->{volumeId},
652             status => $xml->{status},
653             instance_id => $xml->{instanceId},
654             attach_time => $xml->{attachTime},
655             device => $xml->{device},
656 0         0 );
657            
658 0         0 return $attachment;
659             }
660             }
661              
662             =head2 authorize_security_group_ingress(%params)
663              
664             This method adds permissions to a security group. It takes the following parameters:
665              
666             =over
667              
668             =item GroupName (required)
669              
670             The name of the group to add security rules to.
671              
672             =item SourceSecurityGroupName (required when authorizing a user and group together)
673              
674             Name of the group to add access for.
675              
676             =item SourceSecurityGroupOwnerId (required when authorizing a user and group together)
677              
678             Owner of the group to add access for.
679              
680             =item IpProtocol (required when adding access for a CIDR)
681              
682             IP Protocol of the rule you are adding access for (TCP, UDP, or ICMP)
683              
684             =item FromPort (required when adding access for a CIDR)
685              
686             Beginning of port range to add access for.
687              
688             =item ToPort (required when adding access for a CIDR)
689              
690             End of port range to add access for.
691              
692             =item CidrIp (required when adding access for a CIDR)
693              
694             The CIDR IP space we are adding access for.
695              
696             =back
697              
698             Adding a rule can be done in two ways: adding a source group name + source group owner id, or,
699             CIDR IP range. Both methods allow IP protocol, from port and to port specifications.
700              
701             Returns 1 if rule is added successfully.
702              
703             =cut
704              
705             sub authorize_security_group_ingress {
706 0     0 1 0 my $self = shift;
707 0         0 my %args = validate( @_, {
708             GroupName => { type => SCALAR, optional => 1 },
709             GroupId => { type => SCALAR, optional => 1 },
710             SourceSecurityGroupName => {
711             type => SCALAR,
712             depends => ['SourceSecurityGroupOwnerId'],
713             optional => 1 ,
714             },
715             SourceSecurityGroupOwnerId => { type => SCALAR, optional => 1 },
716             IpProtocol => {
717             type => SCALAR,
718             depends => ['FromPort', 'ToPort'],
719             optional => 1
720             },
721             FromPort => { type => SCALAR, optional => 1 },
722             ToPort => { type => SCALAR, optional => 1 },
723             CidrIp => { type => SCALAR, optional => 1 },
724             });
725            
726            
727 0         0 my $xml = $self->_sign(Action => 'AuthorizeSecurityGroupIngress', %args);
728            
729 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
730 0         0 return $self->_parse_errors($xml);
731             }
732             else {
733 0 0       0 if ($xml->{return} eq 'true') {
734 0         0 return 1;
735             }
736             else {
737 0         0 return undef;
738             }
739             }
740             }
741              
742             =head2 bundle_instance(%params)
743              
744             Bundles the Windows instance. This procedure is not applicable for Linux and UNIX instances.
745              
746             NOTE NOTE NOTE This is not well tested as I don't run windows instances
747              
748             =over
749              
750             =item InstanceId (required)
751              
752             The ID of the instance to bundle.
753              
754             =item Storage.S3.Bucket (required)
755              
756             The bucket in which to store the AMI. You can specify a bucket that you already own or a new bucket that Amazon EC2 creates on your behalf. If you specify a bucket that belongs to someone else, Amazon EC2 returns an error.
757              
758             =item Storage.S3.Prefix (required)
759              
760             Specifies the beginning of the file name of the AMI.
761              
762             =item Storage.S3.AWSAccessKeyId (required)
763              
764             The Access Key ID of the owner of the Amazon S3 bucket.
765              
766             =item Storage.S3.UploadPolicy (required)
767              
768             An Amazon S3 upload policy that gives Amazon EC2 permission to upload items into Amazon S3 on the user's behalf.
769              
770             =item Storage.S3.UploadPolicySignature (required)
771              
772             The signature of the Base64 encoded JSON document.
773              
774             JSON Parameters: (all are required)
775              
776             expiration - The expiration of the policy. Amazon recommends 12 hours or longer.
777             conditions - A list of restrictions on what can be uploaded to Amazon S3. Must contain the bucket and ACL conditions in this table.
778             bucket - The bucket to store the AMI.
779             acl - This must be set to ec2-bundle-read.
780              
781             =back
782              
783             Returns a Net::Amazon::EC2::BundleInstanceResponse object
784              
785             =cut
786              
787             sub bundle_instance {
788 0     0 1 0 my $self = shift;
789 0         0 my %args = validate( @_, {
790             'InstanceId' => { type => SCALAR },
791             'Storage.S3.Bucket' => { type => SCALAR },
792             'Storage.S3.Prefix' => { type => SCALAR },
793             'Storage.S3.AWSAccessKeyId' => { type => SCALAR },
794             'Storage.S3.UploadPolicy' => { type => SCALAR },
795             'Storage.S3.UploadPolicySignature' => { type => SCALAR },
796             });
797              
798 0         0 my $xml = $self->_sign(Action => 'BundleInstance', %args);
799            
800 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
801 0         0 return $self->_parse_errors($xml);
802             }
803             else {
804             my $bundle = Net::Amazon::EC2::BundleInstanceResponse->new(
805             instance_id => $xml->{bundleInstanceTask}{instanceId},
806             bundle_id => $xml->{bundleInstanceTask}{bundleId},
807             state => $xml->{bundleInstanceTask}{state},
808             start_time => $xml->{bundleInstanceTask}{startTime},
809             update_time => $xml->{bundleInstanceTask}{updateTime},
810             progress => $xml->{bundleInstanceTask}{progress},
811             s3_bucket => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
812             s3_prefix => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
813             s3_aws_access_key_id => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
814             s3_upload_policy => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
815             s3_policy_upload_signature => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
816             bundle_error_code => $xml->{bundleInstanceTask}{error}{code},
817             bundle_error_message => $xml->{bundleInstanceTask}{error}{message},
818 0         0 );
819            
820 0         0 return $bundle;
821             }
822             }
823              
824             =head2 cancel_bundle_task(%params)
825              
826             Cancels the bundle task. This procedure is not applicable for Linux and UNIX instances.
827              
828             =over
829              
830             =item BundleId (required)
831              
832             The ID of the bundle task to cancel.
833              
834             =back
835              
836             Returns a Net::Amazon::EC2::BundleInstanceResponse object
837              
838             =cut
839              
840             sub cancel_bundle_task {
841 0     0 1 0 my $self = shift;
842 0         0 my %args = validate( @_, {
843             'BundleId' => { type => SCALAR },
844             });
845              
846 0         0 my $xml = $self->_sign(Action => 'CancelBundleTask', %args);
847            
848 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
849 0         0 return $self->_parse_errors($xml);
850             }
851             else {
852             my $bundle = Net::Amazon::EC2::BundleInstanceResponse->new(
853             instance_id => $xml->{bundleInstanceTask}{instanceId},
854             bundle_id => $xml->{bundleInstanceTask}{bundleId},
855             state => $xml->{bundleInstanceTask}{state},
856             start_time => $xml->{bundleInstanceTask}{startTime},
857             update_time => $xml->{bundleInstanceTask}{updateTime},
858             progress => $xml->{bundleInstanceTask}{progress},
859             s3_bucket => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
860             s3_prefix => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
861             s3_aws_access_key_id => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
862             s3_upload_policy => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
863             s3_policy_upload_signature => $xml->{bundleInstanceTask}{storage}{S3}{bucket},
864             bundle_error_code => $xml->{bundleInstanceTask}{error}{code},
865             bundle_error_message => $xml->{bundleInstanceTask}{error}{message},
866 0         0 );
867            
868 0         0 return $bundle;
869             }
870             }
871              
872             =head2 confirm_product_instance(%params)
873              
874             Checks to see if the product code passed in is attached to the instance id, taking the following parameter:
875              
876             =over
877              
878             =item ProductCode (required)
879              
880             The Product Code to check
881              
882             =item InstanceId (required)
883              
884             The Instance Id to check
885              
886             =back
887              
888             Returns a Net::Amazon::EC2::ConfirmProductInstanceResponse object
889              
890             =cut
891              
892             sub confirm_product_instance {
893 0     0 1 0 my $self = shift;
894 0         0 my %args = validate( @_, {
895             ProductCode => { type => SCALAR },
896             InstanceId => { type => SCALAR },
897             });
898              
899 0         0 my $xml = $self->_sign(Action => 'ConfirmProductInstance', %args);
900            
901 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
902 0         0 return $self->_parse_errors($xml);
903             }
904             else {
905             my $confirm_response = Net::Amazon::EC2::ConfirmProductInstanceResponse->new(
906             'return' => $xml->{'return'},
907             owner_id => $xml->{ownerId},
908 0         0 );
909            
910 0         0 return $confirm_response;
911             }
912             }
913              
914             =head2 create_image(%params)
915              
916             Creates an AMI that uses an Amazon EBS root device from a "running" or "stopped" instance.
917              
918             AMIs that use an Amazon EBS root device boot faster than AMIs that use instance stores.
919             They can be up to 1 TiB in size, use storage that persists on instance failure, and can be stopped and started.
920              
921             =over
922              
923             =item InstanceId (required)
924              
925             The ID of the instance.
926              
927             =item Name (required)
928              
929             The name of the AMI that was provided during image creation.
930              
931             Note that the image name has the following constraints:
932              
933             3-128 alphanumeric characters, parenthesis, commas, slashes, dashes, or underscores.
934              
935             =item Description (optional)
936              
937             The description of the AMI that was provided during image creation.
938              
939             =item NoReboot (optional)
940              
941             By default this property is set to false, which means Amazon EC2 attempts to cleanly shut down the
942             instance before image creation and reboots the instance afterwards. When set to true, Amazon EC2
943             does not shut down the instance before creating the image. When this option is used, file system
944             integrity on the created image cannot be guaranteed.
945              
946             =item BlockDeviceMapping (optional)
947              
948             Array ref of the device names exposed to the instance.
949              
950             You can specify device names as '<device>=<block_device>' similar to ec2-create-image command. (L<http://docs.aws.amazon.com/AWSEC2/latest/CommandLineReference/ApiReference-cmd-CreateImage.html>)
951              
952             BlockDeviceMapping => [
953             '/dev/sda=:256:true:standard',
954             '/dev/sdb=none',
955             '/dev/sdc=ephemeral0',
956             '/dev/sdd=ephemeral1',
957             ],
958              
959             =back
960              
961             Returns the ID of the AMI created.
962              
963             =cut
964              
965             sub create_image {
966 0     0 1 0 my $self = shift;
967 0         0 my %args = validate( @_, {
968             InstanceId => { type => SCALAR },
969             Name => { type => SCALAR },
970             Description => { type => SCALAR, optional => 1 },
971             NoReboot => { type => SCALAR, optional => 1 },
972             BlockDeviceMapping => { type => ARRAYREF, optional => 1 },
973             });
974            
975              
976 0 0       0 if (my $bdm = delete $args{BlockDeviceMapping}) {
977 0         0 my $n = 0;
978 0         0 for my $bdme (@$bdm) {
979 0         0 my($device, $block_device) = split /=/, $bdme, 2;
980 0         0 $args{"BlockDeviceMapping.${n}.DeviceName"} = $device;
981              
982 0 0       0 if ($block_device =~ /^ephemeral[0-9]+$/) {
    0          
983 0         0 $args{"BlockDeviceMapping.${n}.VirtualName"} = $block_device;
984             } elsif ($block_device eq 'none') {
985 0         0 $args{"BlockDeviceMapping.${n}.NoDevice"} = '';
986             } else {
987 0         0 my @keys = qw(
988             Ebs.SnapshotId
989             Ebs.VolumeSize
990             Ebs.DeleteOnTermination
991             Ebs.VolumeType
992             Ebs.Iops
993             );
994 0         0 for my $bde (split /:/, $block_device) {
995 0         0 my $key = shift @keys;
996 0 0       0 next unless $bde;
997 0         0 $args{"BlockDeviceMapping.${n}.${key}"} = $bde;
998             }
999             }
1000              
1001 0         0 $n++;
1002             }
1003             }
1004              
1005 0         0 my $xml = $self->_sign(Action => 'CreateImage', %args);
1006              
1007 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1008 0         0 return $self->_parse_errors($xml);
1009             }
1010             else {
1011 0         0 return $xml->{imageId};
1012             }
1013             }
1014              
1015             =head2 create_key_pair(%params)
1016              
1017             Creates a new 2048 bit key pair, taking the following parameter:
1018              
1019             =over
1020              
1021             =item KeyName (required)
1022              
1023             A name for this key. Should be unique.
1024              
1025             =back
1026              
1027             Returns a Net::Amazon::EC2::KeyPair object
1028              
1029             =cut
1030              
1031             sub create_key_pair {
1032 0     0 1 0 my $self = shift;
1033 0         0 my %args = validate( @_, {
1034             KeyName => { type => SCALAR },
1035             });
1036            
1037 0         0 my $xml = $self->_sign(Action => 'CreateKeyPair', %args);
1038              
1039 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1040 0         0 return $self->_parse_errors($xml);
1041             }
1042             else {
1043             my $key_pair = Net::Amazon::EC2::KeyPair->new(
1044             key_name => $xml->{keyName},
1045             key_fingerprint => $xml->{keyFingerprint},
1046             key_material => $xml->{keyMaterial},
1047 0         0 );
1048            
1049 0         0 return $key_pair;
1050             }
1051             }
1052              
1053             =head2 create_security_group(%params)
1054              
1055             This method creates a new security group. It takes the following parameters:
1056              
1057             =over
1058              
1059             =item GroupName (required)
1060              
1061             The name of the new group to create.
1062              
1063             =item GroupDescription (required)
1064              
1065             A short description of the new group.
1066              
1067             =back
1068              
1069             Returns 1 if the group creation succeeds.
1070              
1071             =cut
1072              
1073             sub create_security_group {
1074 0     0 1 0 my $self = shift;
1075 0         0 my %args = validate( @_, {
1076             GroupName => { type => SCALAR },
1077             GroupDescription => { type => SCALAR },
1078             });
1079            
1080            
1081 0         0 my $xml = $self->_sign(Action => 'CreateSecurityGroup', %args);
1082              
1083 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1084 0         0 return $self->_parse_errors($xml);
1085             }
1086             else {
1087 0 0       0 if ($xml->{return} eq 'true') {
1088 0         0 return 1;
1089             }
1090             else {
1091 0         0 return undef;
1092             }
1093             }
1094             }
1095              
1096             =head2 create_snapshot(%params)
1097              
1098             Create a snapshot of a volume. It takes the following arguments:
1099              
1100             =over
1101              
1102             =item VolumeId (required)
1103              
1104             The volume id of the volume you want to take a snapshot of.
1105              
1106             =item Description (optional)
1107              
1108             Description of the Amazon EBS snapshot.
1109              
1110             =back
1111              
1112             Returns a Net::Amazon::EC2::Snapshot object of the newly created snapshot.
1113              
1114             =cut
1115              
1116             sub create_snapshot {
1117 0     0 1 0 my $self = shift;
1118 0         0 my %args = validate( @_, {
1119             VolumeId => { type => SCALAR },
1120             Description => { type => SCALAR, optional => 1 },
1121             });
1122            
1123 0         0 my $xml = $self->_sign(Action => 'CreateSnapshot', %args);
1124              
1125            
1126 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1127 0         0 return $self->_parse_errors($xml);
1128             }
1129             else {
1130 0 0 0     0 unless ( grep { defined && length } $xml->{progress} and ref $xml->{progress} ne 'HASH') {
  0 0       0  
1131 0         0 $xml->{progress} = undef;
1132             }
1133              
1134             my $snapshot = Net::Amazon::EC2::Snapshot->new(
1135             snapshot_id => $xml->{snapshotId},
1136             status => $xml->{status},
1137             volume_id => $xml->{volumeId},
1138             start_time => $xml->{startTime},
1139             progress => $xml->{progress},
1140             owner_id => $xml->{ownerId},
1141             volume_size => $xml->{volumeSize},
1142             description => $xml->{description},
1143 0         0 );
1144              
1145 0         0 return $snapshot;
1146             }
1147             }
1148              
1149             =head2 create_tags(%params)
1150              
1151             Creates tags.
1152              
1153             =over
1154              
1155             =item ResourceId (required)
1156              
1157             The ID of the resource to create tags. Can be a scalar or arrayref
1158              
1159             =item Tags (required)
1160              
1161             Hashref where keys and values will be set on all resources given in the first element.
1162              
1163             =back
1164              
1165             Returns true if the tag creation succeeded.
1166              
1167             =cut
1168              
1169             sub create_tags {
1170 0     0 1 0 my $self = shift;
1171 0         0 my %args = validate( @_, {
1172             ResourceId => { type => ARRAYREF | SCALAR },
1173             Tags => { type => HASHREF },
1174             });
1175              
1176 0 0       0 if (ref ($args{'ResourceId'}) eq 'ARRAY') {
1177 0         0 my $keys = delete $args{'ResourceId'};
1178 0         0 my $count = 1;
1179 0         0 foreach my $key (@{$keys}) {
  0         0  
1180 0         0 $args{"ResourceId." . $count} = $key;
1181 0         0 $count++;
1182             }
1183             }
1184             else {
1185 0         0 $args{"ResourceId.1"} = delete $args{'ResourceId'};
1186             }
1187              
1188 0 0       0 if (ref ($args{'Tags'}) eq 'HASH') {
1189 0         0 my $count = 1;
1190 0         0 my $tags = delete $args{'Tags'};
1191 0         0 foreach my $key ( keys %{$tags} ) {
  0         0  
1192 0 0       0 last if $count > 10;
1193 0         0 $args{"Tag." . $count . ".Key"} = $key;
1194 0         0 $args{"Tag." . $count . ".Value"} = $tags->{$key};
1195 0         0 $count++;
1196             }
1197             }
1198              
1199 0         0 my $xml = $self->_sign(Action => 'CreateTags', %args);
1200              
1201 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1202 0         0 return $self->_parse_errors($xml);
1203             }
1204             else {
1205 0 0       0 if ($xml->{return} eq 'true') {
1206 0         0 return 1;
1207             }
1208             else {
1209 0         0 return undef;
1210             }
1211             }
1212             }
1213              
1214             =head2 create_volume(%params)
1215              
1216             Creates a volume.
1217              
1218             =over
1219              
1220             =item Size (required)
1221              
1222             The size in GiB ( 1024^3 ) of the volume you want to create.
1223              
1224             =item SnapshotId (optional)
1225              
1226             The optional snapshot id to create the volume from. The volume must
1227             be equal or larger than the snapshot it was created from.
1228              
1229             =item AvailabilityZone (required)
1230              
1231             The availability zone to create the volume in.
1232              
1233             =item VolumeType (optional)
1234              
1235             The volume type: 'standard', 'gp2', or 'io1'. Defaults to 'standard'.
1236              
1237             =item Iops (required if VolumeType is 'io1')
1238              
1239             The number of I/O operations per second (IOPS) that the volume
1240             supports. This is limited to 30 times the volume size with an absolute maximum
1241             of 4000. It's likely these numbers will change in the future.
1242              
1243             Required when the volume type is io1; not used otherwise.
1244              
1245             =item Encrypted (optional)
1246              
1247             Encrypt the volume. EBS encrypted volumes are encrypted on the host using
1248             AWS managed keys. Only some instance types support encrypted volumes. At the
1249             time of writing encrypted volumes are not supported for boot volumes.
1250              
1251             =back
1252              
1253             Returns a Net::Amazon::EC2::Volume object containing the resulting volume
1254             status
1255              
1256             =cut
1257              
1258             sub create_volume {
1259 0     0 1 0 my $self = shift;
1260 0         0 my %args = validate( @_, {
1261             Size => { type => SCALAR },
1262             SnapshotId => { type => SCALAR, optional => 1 },
1263             AvailabilityZone => { type => SCALAR },
1264             VolumeType => { type => SCALAR, optional => 1 },
1265             Iops => { type => SCALAR, optional => 1 },
1266             Encrypted => { type => SCALAR, optional => 1 },
1267              
1268             });
1269              
1270 0         0 my $xml = $self->_sign(Action => 'CreateVolume', %args);
1271              
1272            
1273 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1274 0         0 return $self->_parse_errors($xml);
1275             }
1276             else {
1277              
1278 0 0 0     0 unless ( grep { defined && length } $xml->{snapshotId} and ref $xml->{snapshotId} ne 'HASH') {
  0 0       0  
1279 0         0 $xml->{snapshotId} = undef;
1280             }
1281              
1282             my $volume = Net::Amazon::EC2::Volume->new(
1283             volume_id => $xml->{volumeId},
1284             status => $xml->{status},
1285             zone => $xml->{availabilityZone},
1286             create_time => $xml->{createTime},
1287             snapshot_id => $xml->{snapshotId},
1288             size => $xml->{size},
1289             volume_type => $xml->{volumeType},
1290             iops => $xml->{iops},
1291             encrypted => $xml->{encrypted},
1292 0         0 );
1293              
1294 0         0 return $volume;
1295             }
1296             }
1297              
1298             =head2 delete_key_pair(%params)
1299              
1300             This method deletes a keypair. Takes the following parameter:
1301              
1302             =over
1303              
1304             =item KeyName (required)
1305              
1306             The name of the key to delete.
1307              
1308             =back
1309              
1310             Returns 1 if the key was successfully deleted.
1311              
1312             =cut
1313              
1314             sub delete_key_pair {
1315 0     0 1 0 my $self = shift;
1316 0         0 my %args = validate( @_, {
1317             KeyName => { type => SCALAR },
1318             });
1319            
1320 0         0 my $xml = $self->_sign(Action => 'DeleteKeyPair', %args);
1321              
1322 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1323 0         0 return $self->_parse_errors($xml);
1324             }
1325             else {
1326 0 0       0 if ($xml->{return} eq 'true') {
1327 0         0 return 1;
1328             }
1329             else {
1330 0         0 return undef;
1331             }
1332             }
1333             }
1334              
1335             =head2 delete_security_group(%params)
1336              
1337             This method deletes a security group. It takes the following parameter:
1338              
1339             =over
1340              
1341             =item GroupName (required)
1342              
1343             The name of the security group to delete.
1344              
1345             =back
1346              
1347             Returns 1 if the delete succeeded.
1348              
1349             =cut
1350              
1351             sub delete_security_group {
1352 0     0 1 0 my $self = shift;
1353 0         0 my %args = validate( @_, {
1354             GroupName => { type => SCALAR },
1355             });
1356            
1357            
1358 0         0 my $xml = $self->_sign(Action => 'DeleteSecurityGroup', %args);
1359            
1360 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1361 0         0 return $self->_parse_errors($xml);
1362             }
1363             else {
1364 0 0       0 if ($xml->{return} eq 'true') {
1365 0         0 return 1;
1366             }
1367             else {
1368 0         0 return undef;
1369             }
1370             }
1371             }
1372              
1373             =head2 delete_snapshot(%params)
1374              
1375             Deletes the snapshots passed in. It takes the following arguments:
1376              
1377             =over
1378              
1379             =item SnapshotId (required)
1380              
1381             A snapshot id can be passed in. Will delete the corresponding snapshot.
1382              
1383             =back
1384              
1385             Returns true if the deleting succeeded.
1386              
1387             =cut
1388              
1389             sub delete_snapshot {
1390 0     0 1 0 my $self = shift;
1391 0         0 my %args = validate( @_, {
1392             SnapshotId => { type => SCALAR },
1393             });
1394              
1395 0         0 my $xml = $self->_sign(Action => 'DeleteSnapshot', %args);
1396              
1397 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1398 0         0 return $self->_parse_errors($xml);
1399             }
1400             else {
1401 0 0       0 if ($xml->{return} eq 'true') {
1402 0         0 return 1;
1403             }
1404             else {
1405 0         0 return undef;
1406             }
1407             }
1408             }
1409              
1410             =head2 delete_volume(%params)
1411              
1412             Delete a volume.
1413              
1414             =over
1415              
1416             =item VolumeId (required)
1417              
1418             The volume id you wish to delete.
1419              
1420             =back
1421              
1422             Returns true if the deleting succeeded.
1423              
1424             =cut
1425              
1426             sub delete_volume {
1427 0     0 1 0 my $self = shift;
1428 0         0 my %args = validate( @_, {
1429             VolumeId => { type => SCALAR, optional => 1 },
1430             });
1431              
1432 0         0 my $xml = $self->_sign(Action => 'DeleteVolume', %args);
1433              
1434            
1435 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1436 0         0 return $self->_parse_errors($xml);
1437             }
1438             else {
1439 0 0       0 if ($xml->{return} eq 'true') {
1440 0         0 return 1;
1441             }
1442             else {
1443 0         0 return undef;
1444             }
1445             }
1446             }
1447              
1448             =head2 delete_tags(%params)
1449              
1450             Delete tags.
1451              
1452             =over
1453              
1454             =item ResourceId (required)
1455              
1456             The ID of the resource to delete tags
1457              
1458             =item Tag.Key (required)
1459              
1460             Key for a tag, may pass in a scalar or arrayref.
1461              
1462             =item Tag.Value (required)
1463              
1464             Value for a tag, may pass in a scalar or arrayref.
1465              
1466             =back
1467              
1468             Returns true if the releasing succeeded.
1469              
1470             =cut
1471              
1472             sub delete_tags {
1473 0     0 1 0 my $self = shift;
1474 0         0 my %args = validate( @_, {
1475             ResourceId => { type => ARRAYREF | SCALAR },
1476             'Tag.Key' => { type => ARRAYREF | SCALAR },
1477             'Tag.Value' => { type => ARRAYREF | SCALAR, optional => 1 },
1478             });
1479              
1480             # If we have a array ref of keys lets split them out into their Tag.n.Key format
1481 0 0       0 if (ref ($args{'Tag.Key'}) eq 'ARRAY') {
1482 0         0 my $keys = delete $args{'Tag.Key'};
1483 0         0 my $count = 1;
1484 0         0 foreach my $key (@{$keys}) {
  0         0  
1485 0         0 $args{"Tag." . $count . ".Key"} = $key;
1486 0         0 $count++;
1487             }
1488             }
1489              
1490             # If we have a array ref of values lets split them out into their Tag.n.Value format
1491 0 0       0 if (ref ($args{'Tag.Value'}) eq 'ARRAY') {
1492 0         0 my $values = delete $args{'Tag.Value'};
1493 0         0 my $count = 1;
1494 0         0 foreach my $value (@{$values}) {
  0         0  
1495 0         0 $args{"Tag." . $count . ".Value"} = $value;
1496 0         0 $count++;
1497             }
1498             }
1499              
1500 0         0 my $xml = $self->_sign(Action => 'DeleteTags', %args);
1501              
1502 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1503 0         0 return $self->_parse_errors($xml);
1504             }
1505             else {
1506 0 0       0 if ($xml->{return} eq 'true') {
1507 0         0 return 1;
1508             }
1509             else {
1510 0         0 return undef;
1511             }
1512             }
1513             }
1514              
1515              
1516             =head2 deregister_image(%params)
1517              
1518             This method will deregister an AMI. It takes the following parameter:
1519              
1520             =over
1521              
1522             =item ImageId (required)
1523              
1524             The image id of the AMI you want to deregister.
1525              
1526             =back
1527              
1528             Returns 1 if the deregistering succeeded
1529              
1530             =cut
1531              
1532             sub deregister_image {
1533 0     0 1 0 my $self = shift;
1534 0         0 my %args = validate( @_, {
1535             ImageId => { type => SCALAR },
1536             });
1537            
1538              
1539 0         0 my $xml = $self->_sign(Action => 'DeregisterImage', %args);
1540              
1541 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1542 0         0 return $self->_parse_errors($xml);
1543             }
1544             else {
1545 0 0       0 if ($xml->{return} eq 'true') {
1546 0         0 return 1;
1547             }
1548             else {
1549 0         0 return undef;
1550             }
1551             }
1552             }
1553              
1554             =head2 describe_addresses(%params)
1555              
1556             This method describes the elastic addresses currently allocated and any instances associated with them. It takes the following arguments:
1557              
1558             =over
1559              
1560             =item PublicIp (optional)
1561              
1562             The IP address to describe. Can be either a scalar or an array ref.
1563              
1564             =back
1565              
1566             Returns an array ref of Net::Amazon::EC2::DescribeAddress objects
1567              
1568             =cut
1569              
1570             sub describe_addresses {
1571 0     0 1 0 my $self = shift;
1572 0         0 my %args = validate( @_, {
1573             PublicIp => { type => SCALAR | ARRAYREF, optional => 1 },
1574             });
1575              
1576             # If we have a array ref of ip addresses lets split them out into their PublicIp.n format
1577 0 0       0 if (ref ($args{PublicIp}) eq 'ARRAY') {
1578 0         0 my $ip_addresses = delete $args{PublicIp};
1579 0         0 my $count = 1;
1580 0         0 foreach my $ip_address (@{$ip_addresses}) {
  0         0  
1581 0         0 $args{"PublicIp." . $count} = $ip_address;
1582 0         0 $count++;
1583             }
1584             }
1585            
1586 0         0 my $addresses;
1587 0         0 my $xml = $self->_sign(Action => 'DescribeAddresses', %args);
1588            
1589 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1590 0         0 return $self->_parse_errors($xml);
1591             }
1592             else {
1593 0         0 foreach my $addy (@{$xml->{addressesSet}{item}}) {
  0         0  
1594 0 0       0 if (ref($addy->{instanceId}) eq 'HASH') {
1595 0         0 undef $addy->{instanceId};
1596             }
1597            
1598             my $address = Net::Amazon::EC2::DescribeAddress->new(
1599             public_ip => $addy->{publicIp},
1600             instance_id => $addy->{instanceId},
1601 0         0 );
1602            
1603 0         0 push @$addresses, $address;
1604             }
1605            
1606 0         0 return $addresses;
1607             }
1608             }
1609              
1610             =head2 describe_availability_zones(%params)
1611              
1612             This method describes the availability zones currently available to choose from. It takes the following arguments:
1613              
1614             =over
1615              
1616             =item ZoneName (optional)
1617              
1618             The zone name to describe. Can be either a scalar or an array ref.
1619              
1620             =back
1621              
1622             Returns an array ref of Net::Amazon::EC2::AvailabilityZone objects
1623              
1624             =cut
1625              
1626             sub describe_availability_zones {
1627 0     0 1 0 my $self = shift;
1628 0         0 my %args = validate( @_, {
1629             ZoneName => { type => SCALAR | ARRAYREF, optional => 1 },
1630             });
1631              
1632             # If we have a array ref of zone names lets split them out into their ZoneName.n format
1633 0 0       0 if (ref ($args{ZoneName}) eq 'ARRAY') {
1634 0         0 my $zone_names = delete $args{ZoneName};
1635 0         0 my $count = 1;
1636 0         0 foreach my $zone_name (@{$zone_names}) {
  0         0  
1637 0         0 $args{"ZoneName." . $count} = $zone_name;
1638 0         0 $count++;
1639             }
1640             }
1641            
1642 0         0 my $xml = $self->_sign(Action => 'DescribeAvailabilityZones', %args);
1643              
1644 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1645 0         0 return $self->_parse_errors($xml);
1646             }
1647             else {
1648 0         0 my $availability_zones;
1649 0         0 foreach my $az (@{$xml->{availabilityZoneInfo}{item}}) {
  0         0  
1650 0         0 my $availability_zone_messages;
1651             # Create the messages for this zone
1652 0         0 foreach my $azm (@{$az->{messageSet}{item}}) {
  0         0  
1653             my $availability_zone_message = Net::Amazon::EC2::AvailabilityZoneMessage->new(
1654             message => $azm->{message},
1655 0         0 );
1656            
1657 0         0 push @$availability_zone_messages, $availability_zone_message;
1658             }
1659            
1660             my $availability_zone = Net::Amazon::EC2::AvailabilityZone->new(
1661             zone_name => $az->{zoneName},
1662             zone_state => $az->{zoneState},
1663             region_name => $az->{regionName},
1664 0         0 messages => $availability_zone_messages,
1665             );
1666            
1667 0         0 push @$availability_zones, $availability_zone;
1668             }
1669            
1670 0         0 return $availability_zones;
1671             }
1672             }
1673              
1674             =head2 describe_bundle_tasks(%params)
1675              
1676             Describes current bundling tasks. This procedure is not applicable for Linux and UNIX instances.
1677              
1678             =over
1679              
1680             =item BundleId (optional)
1681              
1682             The optional ID of the bundle task to describe.
1683              
1684             =back
1685              
1686             Returns a array ref of Net::Amazon::EC2::BundleInstanceResponse objects
1687              
1688             =cut
1689              
1690             sub describe_bundle_tasks {
1691 0     0 1 0 my $self = shift;
1692 0         0 my %args = validate( @_, {
1693             'BundleId' => { type => SCALAR, optional => 1 },
1694             });
1695              
1696 0         0 my $xml = $self->_sign(Action => 'DescribeBundleTasks', %args);
1697            
1698 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1699 0         0 return $self->_parse_errors($xml);
1700             }
1701             else {
1702 0         0 my $bundle_tasks;
1703            
1704 0         0 foreach my $item (@{$xml->{bundleInstanceTasksSet}{item}}) {
  0         0  
1705             my $bundle = Net::Amazon::EC2::BundleInstanceResponse->new(
1706             instance_id => $item->{instanceId},
1707             bundle_id => $item->{bundleId},
1708             state => $item->{state},
1709             start_time => $item->{startTime},
1710             update_time => $item->{updateTime},
1711             progress => $item->{progress},
1712             s3_bucket => $item->{storage}{S3}{bucket},
1713             s3_prefix => $item->{storage}{S3}{bucket},
1714             s3_aws_access_key_id => $item->{storage}{S3}{bucket},
1715             s3_upload_policy => $item->{storage}{S3}{bucket},
1716             s3_policy_upload_signature => $item->{storage}{S3}{bucket},
1717             bundle_error_code => $item->{error}{code},
1718             bundle_error_message => $item->{error}{message},
1719 0         0 );
1720            
1721 0         0 push @$bundle_tasks, $bundle;
1722             }
1723            
1724 0         0 return $bundle_tasks;
1725             }
1726             }
1727              
1728             =head2 describe_image_attributes(%params)
1729              
1730             This method pulls a list of attributes for the image id specified
1731              
1732             =over
1733              
1734             =item ImageId (required)
1735              
1736             A scalar containing the image you want to get the list of attributes for.
1737              
1738             =item Attribute (required)
1739              
1740             A scalar containing the attribute to describe.
1741              
1742             Valid attributes are:
1743              
1744             =over
1745              
1746             =item launchPermission - The AMIs launch permissions.
1747              
1748             =item ImageId - ID of the AMI for which an attribute will be described.
1749              
1750             =item productCodes - The product code attached to the AMI.
1751              
1752             =item kernel - Describes the ID of the kernel associated with the AMI.
1753              
1754             =item ramdisk - Describes the ID of RAM disk associated with the AMI.
1755              
1756             =item blockDeviceMapping - Defines native device names to use when exposing virtual devices.
1757              
1758             =item platform - Describes the operating system platform.
1759              
1760             =back
1761              
1762             =back
1763              
1764             Returns a Net::Amazon::EC2::DescribeImageAttribute object
1765              
1766             * NOTE: There is currently a bug in Amazon's SOAP and Query API
1767             for when you try and describe the attributes: kernel, ramdisk, blockDeviceMapping, or platform
1768             AWS returns an invalid response. No response yet from Amazon on an ETA for getting that bug fixed.
1769              
1770             =cut
1771              
1772             sub describe_image_attribute {
1773 0     0 0 0 my $self = shift;
1774 0         0 my %args = validate( @_, {
1775             ImageId => { type => SCALAR },
1776             Attribute => { type => SCALAR }
1777             });
1778            
1779 0         0 my $xml = $self->_sign(Action => 'DescribeImageAttribute', %args);
1780            
1781 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1782 0         0 return $self->_parse_errors($xml);
1783             }
1784             else {
1785 0         0 my $launch_permissions;
1786             my $product_codes;
1787 0         0 my $block_device_mappings;
1788            
1789 0 0       0 if ( grep { defined && length } $xml->{launchPermission}{item} ) {
  0 0       0  
1790 0         0 foreach my $lp (@{$xml->{launchPermission}{item}}) {
  0         0  
1791             my $launch_permission = Net::Amazon::EC2::LaunchPermission->new(
1792             group => $lp->{group},
1793             user_id => $lp->{userId},
1794 0         0 );
1795            
1796 0         0 push @$launch_permissions, $launch_permission;
1797             }
1798             }
1799              
1800 0 0       0 if ( grep { defined && length } $xml->{productCodes}{item} ) {
  0 0       0  
1801 0         0 foreach my $pc (@{$xml->{productCodes}{item}}) {
  0         0  
1802             my $product_code = Net::Amazon::EC2::ProductCode->new(
1803             product_code => $pc->{productCode},
1804 0         0 );
1805            
1806 0         0 push @$product_codes, $product_code;
1807             }
1808             }
1809            
1810 0 0       0 if ( grep { defined && length } $xml->{blockDeviceMapping}{item} ) {
  0 0       0  
1811 0         0 foreach my $bd (@{$xml->{blockDeviceMapping}{item}}) {
  0         0  
1812             my $block_device_mapping = Net::Amazon::EC2::BlockDeviceMapping->new(
1813             virtual_name => $bd->{virtualName},
1814             device_name => $bd->{deviceName},
1815 0         0 );
1816            
1817 0         0 push @$block_device_mappings, $block_device_mapping;
1818             }
1819             }
1820            
1821             my $describe_image_attribute = Net::Amazon::EC2::DescribeImageAttribute->new(
1822             image_id => $xml->{imageId},
1823             launch_permissions => $launch_permissions,
1824             product_codes => $product_codes,
1825             kernel => $xml->{kernel},
1826             ramdisk => $xml->{ramdisk},
1827             blockDeviceMapping => $block_device_mappings,
1828             platform => $xml->{platform},
1829 0         0 );
1830              
1831 0         0 return $describe_image_attribute;
1832             }
1833             }
1834              
1835             =head2 describe_images(%params)
1836              
1837             This method pulls a list of the AMIs which can be run. The list can be modified by passing in some of the following parameters:
1838              
1839             =over
1840              
1841             =item ImageId (optional)
1842              
1843             Either a scalar or an array ref can be passed in, will cause just these AMIs to be 'described'
1844              
1845             =item Owner (optional)
1846              
1847             Either a scalar or an array ref can be passed in, will cause AMIs owned by the Owner's provided will be 'described'. Pass either account ids, or 'amazon' for all amazon-owned AMIs, or 'self' for your own AMIs.
1848              
1849             =item ExecutableBy (optional)
1850              
1851             Either a scalar or an array ref can be passed in, will cause AMIs executable by the account id's specified. Or 'self' for your own AMIs.
1852              
1853             =back
1854              
1855             Returns an array ref of Net::Amazon::EC2::DescribeImagesResponse objects
1856              
1857             =cut
1858              
1859             sub describe_images {
1860 0     0 1 0 my $self = shift;
1861 0         0 my %args = validate( @_, {
1862             ImageId => { type => SCALAR | ARRAYREF, optional => 1 },
1863             Owner => { type => SCALAR | ARRAYREF, optional => 1 },
1864             ExecutableBy => { type => SCALAR | ARRAYREF, optional => 1 },
1865             });
1866            
1867             # If we have a array ref of instances lets split them out into their ImageId.n format
1868 0 0       0 if (ref ($args{ImageId}) eq 'ARRAY') {
1869 0         0 my $image_ids = delete $args{ImageId};
1870 0         0 my $count = 1;
1871 0         0 foreach my $image_id (@{$image_ids}) {
  0         0  
1872 0         0 $args{"ImageId." . $count} = $image_id;
1873 0         0 $count++;
1874             }
1875             }
1876            
1877             # If we have a array ref of instances lets split them out into their Owner.n format
1878 0 0       0 if (ref ($args{Owner}) eq 'ARRAY') {
1879 0         0 my $owners = delete $args{Owner};
1880 0         0 my $count = 1;
1881 0         0 foreach my $owner (@{$owners}) {
  0         0  
1882 0         0 $args{"Owner." . $count} = $owner;
1883 0         0 $count++;
1884             }
1885             }
1886              
1887             # If we have a array ref of instances lets split them out into their ExecutableBy.n format
1888 0 0       0 if (ref ($args{ExecutableBy}) eq 'ARRAY') {
1889 0         0 my $executors = delete $args{ExecutableBy};
1890 0         0 my $count = 1;
1891 0         0 foreach my $executor (@{$executors}) {
  0         0  
1892 0         0 $args{"ExecutableBy." . $count} = $executor;
1893 0         0 $count++;
1894             }
1895             }
1896              
1897 0         0 my $xml = $self->_sign(Action => 'DescribeImages', %args);
1898            
1899 0 0       0 if ( grep { defined && length } $xml->{Errors} ) {
  0 0       0  
1900 0         0 return $self->_parse_errors($xml);
1901             }
1902             else {
1903 0         0 my $images;
1904            
1905 0         0 foreach my $item (@{$xml->{imagesSet}{item}}) {
  0         0  
1906 0         0 my $product_codes;
1907             my $state_reason;
1908 0         0 my $block_device_mappings;
1909            
1910 0 0       0 if ( grep { defined && length } $item->{stateReason} ) {
  0 0       0  
1911             $state_reason = Net::Amazon::EC2::StateReason->new(
1912             code => $item->{stateReason}{code},
1913             message => $item->{stateReason}{message},
1914 0         0 );
1915             }
1916              
1917 0 0       0 if ( grep { defined && length } $item->{blockDeviceMapping} ) {
  0 0       0  
1918 0         0 foreach my $bdm ( @{$item->{blockDeviceMapping}{item}} ) {
  0         0  
1919 0         0 my $virtual_name;
1920             my $no_device;
1921 0         0 my $ebs_block_device_mapping;
1922            
1923 0 0       0 if ( grep { defined && length } $bdm->{ebs} ) {
  0 0       0  
1924             $ebs_block_device_mapping = Net::Amazon::EC2::EbsBlockDevice->new(
1925             snapshot_id => $bdm->{ebs}{snapshotId},
1926             volume_size => $bdm->{ebs}{volumeSize},
1927             delete_on_termination => $bdm->{ebs}{deleteOnTermination},
1928 0         0 );
1929             }
1930            
1931            
1932             my $block_device_mapping = Net::Amazon::EC2::BlockDeviceMapping->new(
1933             device_name => $bdm->{deviceName},
1934 0         0 virtual_name => $virtual_name,
1935             ebs => $ebs_block_device_mapping,
1936             no_device => $no_device,
1937             );
1938 0         0 push @$block_device_mappings, $block_device_mapping;
1939             }
1940             }
1941 0 0       0 $item->{description} = undef if ref ($item->{description});
1942              
1943 0         0 my $tag_sets;
1944 0         0 foreach my $tag_arr (@{$item->{tagSet}{item}}) {
  0         0  
1945 0 0       0 if ( ref $tag_arr->{value} eq "HASH" ) {
1946 0         0 $tag_arr->{value} = "";
1947             }
1948             my $tag = Net::Amazon::EC2::TagSet->new(
1949             key => $tag_arr->{key},
1950             value => $tag_arr->{value},
1951 0         0 );
1952 0         0 push @$tag_sets, $tag;
1953             }
1954              
1955             my $image = Net::Amazon::EC2::DescribeImagesResponse->new(
1956             image_id => $item->{imageId},
1957             image_owner_id => $item->{imageOwnerId},
1958             image_state => $item->{imageState},
1959             is_public => $item->{isPublic},
1960             image_location => $item->{imageLocation},
1961             architecture => $item->{architecture},
1962             image_type => $item->{imageType},
1963             kernel_id => $item->{kernelId},
1964             ramdisk_id => $item->{ramdiskId},
1965             platform => $item->{platform},
1966             state_reason => $state_reason,
1967             image_owner_alias => $item->{imageOwnerAlias},
1968             name => $item->{name},
1969             description => $item->{description},
1970             root_device_type => $item->{rootDeviceType},
1971             root_device_name => $item->{rootDeviceName},
1972 0         0 block_device_mapping => $block_device_mappings,
1973             tag_set => $tag_sets,
1974             );
1975            
1976 0 0       0 if (grep { defined && length } $item->{productCodes} ) {
  0 0       0  
1977 0         0 foreach my $pc (@{$item->{productCodes}{item}}) {
  0         0  
1978 0         0 my $product_code = Net::Amazon::EC2::ProductCode->new( product_code => $pc->{productCode} );
1979 0         0 push @$product_codes, $product_code;
1980             }
1981            
1982 0         0 $image->product_codes($product_codes);
1983             }
1984              
1985            
1986 0         0 push @$images, $image;
1987             }
1988            
1989 0         0 return $images;
1990             }
1991             }
1992              
1993             =head2 describe_instances(%params)
1994              
1995             This method pulls a list of the instances which are running or were just running. The list can be modified by passing in some of the following parameters:
1996              
1997             =over
1998              
1999             =item InstanceId (optional)
2000              
2001             Either a scalar or an array ref can be passed in, will cause just these instances to be 'described'
2002              
2003             =item Filter (optional)
2004              
2005             The filters for only the matching instances to be 'described'.
2006             A filter tuple is an arrayref constsing one key and one or more values.
2007             The option takes one filter tuple, or an arrayref of multiple filter tuples.
2008              
2009             =back
2010              
2011             Returns an array ref of Net::Amazon::EC2::ReservationInfo objects
2012              
2013             =cut
2014              
2015             sub describe_instances {
2016 3     3 1 8459 my $self = shift;
2017 3         92 my %args = validate( @_, {
2018             InstanceId => { type => SCALAR | ARRAYREF, optional => 1 },
2019             Filter => { type => ARRAYREF, optional => 1 },
2020             });
2021            
2022             # If we have a array ref of instances lets split them out into their InstanceId.n format
2023 3 50       27 if (ref ($args{InstanceId}) eq 'ARRAY') {
2024 0         0 my $instance_ids = delete $args{InstanceId};
2025 0         0 my $count = 1;
2026 0         0 foreach my $instance_id (@{$instance_ids}) {
  0         0  
2027 0         0 $args{"InstanceId." . $count} = $instance_id;
2028 0         0 $count++;
2029             }
2030             }
2031              
2032 3         16 $self->_build_filters(\%args);
2033 3         15 my $xml = $self->_sign(Action => 'DescribeInstances', %args);
2034 3         8 my $reservations;
2035            
2036 3 50       14 if ( grep { defined && length } $xml->{Errors} ) {
  3 50       34  
2037 3         14 return $self->_parse_errors($xml);
2038             }
2039             else {
2040 0           foreach my $reservation_set (@{$xml->{reservationSet}{item}}) {
  0            
2041 0           my $group_sets=[];
2042 0           foreach my $group_arr (@{$reservation_set->{groupSet}{item}}) {
  0            
2043             my $group = Net::Amazon::EC2::GroupSet->new(
2044             group_id => $group_arr->{groupId},
2045             group_name => $group_arr->{groupName},
2046 0           );
2047 0           push @$group_sets, $group;
2048             }
2049            
2050 0           my $running_instances;
2051 0           foreach my $instance_elem (@{$reservation_set->{instancesSet}{item}}) {
  0            
2052             my $instance_state_type = Net::Amazon::EC2::InstanceState->new(
2053             code => $instance_elem->{instanceState}{code},
2054             name => $instance_elem->{instanceState}{name},
2055 0           );
2056            
2057 0           my $product_codes;
2058             my $block_device_mappings;
2059 0           my $state_reason;
2060 0           my $network_interfaces_set;
2061            
2062 0 0         if (grep { defined && length } $instance_elem->{productCodes} ) {
  0 0          
2063 0           foreach my $pc (@{$instance_elem->{productCodes}{item}}) {
  0            
2064 0           my $product_code = Net::Amazon::EC2::ProductCode->new( product_code => $pc->{productCode} );
2065 0           push @$product_codes, $product_code;
2066             }
2067             }
2068              
2069 0 0         if ( grep { defined && length } $instance_elem->{networkInterfaceSet} ) {
  0 0          
2070 0           foreach my $interface( @{$instance_elem->{networkInterfaceSet}{item}} ) {
  0            
2071             my $network_interface = Net::Amazon::EC2::NetworkInterfaceSet->new(
2072             network_interface_id => $interface->{networkInterfaceId},
2073             subnet_id => $interface->{subnetId},
2074             vpc_id => $interface->{vpcId},
2075             description => $interface->{description},
2076             status => $interface->{status},
2077             mac_address => $interface->{macAddress},
2078             private_ip_address => $interface->{privateIpAddress},
2079 0           );
2080              
2081 0 0         if ( grep { defined && length } $interface->{groupSet} ) {
  0 0          
2082 0           my $groups_set = [];
2083 0           foreach my $group( @{$interface->{groupSet}{item}} ) {
  0            
2084             my $group = Net::Amazon::EC2::GroupSet->new(
2085             group_id => $group->{groupId},
2086             group_name => $group->{groupName},
2087 0           );
2088 0           push @$groups_set, $group;
2089             }
2090              
2091 0           $network_interface->{group_sets} = $groups_set;
2092             }
2093              
2094 0           push @$network_interfaces_set, $network_interface;
2095             }
2096             }
2097              
2098 0 0         if ( grep { defined && length } $instance_elem->{blockDeviceMapping} ) {
  0 0          
2099 0           foreach my $bdm ( @{$instance_elem->{blockDeviceMapping}{item}} ) {
  0            
2100             my $ebs_block_device_mapping = Net::Amazon::EC2::EbsInstanceBlockDeviceMapping->new(
2101             volume_id => $bdm->{ebs}{volumeId},
2102             status => $bdm->{ebs}{status},
2103             attach_time => $bdm->{ebs}{attachTime},
2104             delete_on_termination => $bdm->{ebs}{deleteOnTermination},
2105 0           );
2106            
2107             my $block_device_mapping = Net::Amazon::EC2::BlockDeviceMapping->new(
2108             ebs => $ebs_block_device_mapping,
2109             device_name => $bdm->{deviceName},
2110 0           );
2111 0           push @$block_device_mappings, $block_device_mapping;
2112             }
2113             }
2114              
2115 0 0         if ( grep { defined && length } $instance_elem->{stateReason} ) {
  0 0          
2116             $state_reason = Net::Amazon::EC2::StateReason->new(
2117             code => $instance_elem->{stateReason}{code},
2118             message => $instance_elem->{stateReason}{message},
2119 0           );
2120             }
2121            
2122 0 0 0       unless ( grep { defined && length } $instance_elem->{reason} and ref $instance_elem->{reason} ne 'HASH' ) {
  0 0          
2123 0           $instance_elem->{reason} = undef;
2124             }
2125            
2126 0 0 0       unless ( grep { defined && length } $instance_elem->{privateDnsName} and ref $instance_elem->{privateDnsName} ne 'HASH' ) {
  0 0          
2127 0           $instance_elem->{privateDnsName} = undef;
2128             }
2129            
2130 0 0 0       unless ( grep { defined && length } $instance_elem->{dnsName} and ref $instance_elem->{dnsName} ne 'HASH' ) {
  0 0          
2131 0           $instance_elem->{dnsName} = undef;
2132             }
2133              
2134 0 0 0       unless ( grep { defined && length } $instance_elem->{placement}{availabilityZone} and ref $instance_elem->{placement}{availabilityZone} ne 'HASH' ) {
  0 0          
2135 0           $instance_elem->{placement}{availabilityZone} = undef;
2136             }
2137            
2138 0           my $placement_response = Net::Amazon::EC2::PlacementResponse->new( availability_zone => $instance_elem->{placement}{availabilityZone} );
2139              
2140 0           my $tag_sets;
2141 0           foreach my $tag_arr (@{$instance_elem->{tagSet}{item}}) {
  0            
2142 0 0         if ( ref $tag_arr->{value} eq "HASH" ) {
2143 0           $tag_arr->{value} = "";
2144             }
2145             my $tag = Net::Amazon::EC2::TagSet->new(
2146             key => $tag_arr->{key},
2147             value => $tag_arr->{value},
2148 0           );
2149 0           push @$tag_sets, $tag;
2150             }
2151              
2152             my $running_instance = Net::Amazon::EC2::RunningInstances->new(
2153             ami_launch_index => $instance_elem->{amiLaunchIndex},
2154             dns_name => $instance_elem->{dnsName},
2155             image_id => $instance_elem->{imageId},
2156             kernel_id => $instance_elem->{kernelId},
2157             ramdisk_id => $instance_elem->{ramdiskId},
2158             instance_id => $instance_elem->{instanceId},
2159             instance_state => $instance_state_type,
2160             instance_type => $instance_elem->{instanceType},
2161             key_name => $instance_elem->{keyName},
2162             launch_time => $instance_elem->{launchTime},
2163             placement => $placement_response,
2164             private_dns_name => $instance_elem->{privateDnsName},
2165             reason => $instance_elem->{reason},
2166             platform => $instance_elem->{platform},
2167             monitoring => $instance_elem->{monitoring}{state},
2168             subnet_id => $instance_elem->{subnetId},
2169             vpc_id => $instance_elem->{vpcId},
2170             private_ip_address => $instance_elem->{privateIpAddress},
2171             ip_address => $instance_elem->{ipAddress},
2172             architecture => $instance_elem->{architecture},
2173             root_device_name => $instance_elem->{rootDeviceName},
2174             root_device_type => $instance_elem->{rootDeviceType},
2175 0           block_device_mapping => $block_device_mappings,
2176             state_reason => $state_reason,
2177             tag_set => $tag_sets,
2178             network_interface_set => $network_interfaces_set,
2179             );
2180              
2181 0 0         if ($product_codes) {
2182 0           $running_instance->product_codes($product_codes);
2183             }
2184            
2185 0           push @$running_instances, $running_instance;
2186             }
2187            
2188             my $reservation = Net::Amazon::EC2::ReservationInfo->new(
2189             reservation_id => $reservation_set->{reservationId},
2190             owner_id => $reservation_set->{ownerId},
2191             group_set => $group_sets,
2192             instances_set => $running_instances,
2193             requester_id => $reservation_set->{requesterId},
2194 0           );
2195            
2196 0           push @$reservations, $reservation;
2197             }
2198            
2199             }
2200              
2201 0           return $reservations;
2202             }
2203              
2204             =head2 describe_instance_status(%params)
2205              
2206             This method pulls a list of the instances based on some status filter. The list can be modified by passing in some of the following parameters:
2207              
2208             =over
2209              
2210             =item InstanceId (optional)
2211              
2212             Either a scalar or an array ref can be passed in, will cause just these instances to be 'described'
2213              
2214             =item Filter (optional)
2215              
2216             The filters for only the matching instances to be 'described'.
2217             A filter tuple is an arrayref constsing one key and one or more values.
2218             The option takes one filter tuple, or an arrayref of multiple filter tuples.
2219              
2220             =back
2221              
2222             Returns an array ref of Net::Amazon::EC2::InstanceStatuses objects
2223              
2224             =cut
2225              
2226             sub describe_instance_status {
2227 0     0 1   my $self = shift;
2228 0           my %args = validate(
2229             @_,
2230             {
2231             InstanceId => { type => SCALAR | ARRAYREF, optional => 1 },
2232             Filter => { type => ARRAYREF, optional => 1 },
2233             MaxResults => { type => SCALAR, optional => 1 },
2234             NextToken => { type => SCALAR, optional => 1 },
2235             }
2236             );
2237              
2238             # If we have a array ref of instances lets split them out into their InstanceId.n format
2239 0 0         if ( ref( $args{InstanceId} ) eq 'ARRAY' ) {
2240 0           my $instance_ids = delete $args{InstanceId};
2241 0           my $count = 1;
2242 0           foreach my $instance_id ( @{$instance_ids} ) {
  0            
2243 0           $args{ "InstanceId." . $count } = $instance_id;
2244 0           $count++;
2245             }
2246             }
2247              
2248 0           $self->_build_filters( \%args );
2249 0           my $xml = $self->_sign( Action => 'DescribeInstanceStatus', %args );
2250              
2251 0           my $instancestatuses;
2252             my $token;
2253              
2254 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2255 0           return $self->_parse_errors($xml);
2256             }
2257             else {
2258 0           foreach my $instancestatus_elem ( @{ $xml->{instanceStatusSet}{item} } )
  0            
2259             {
2260 0           my $instance_status = $self->_create_describe_instance_status( $instancestatus_elem );
2261 0           push @$instancestatuses, $instance_status;
2262             }
2263              
2264 0 0         if ( grep { defined && length } $xml->{nextToken} ) {
  0 0          
2265 0           $token = $xml->{nextToken};
2266 0           while(1) {
2267 0           $args{NextToken} = $token;
2268 0           $self->_build_filters( \%args );
2269 0           my $tmp_xml = $self->_sign( Action => 'DescribeInstanceStatus', %args );
2270 0 0         if ( grep { defined && length } $tmp_xml->{Errors} ) {
  0 0          
2271 0           return $self->_parse_errors($tmp_xml);
2272             }
2273             else {
2274 0           foreach my $tmp_instancestatus_elem ( @{ $tmp_xml->{instanceStatusSet}{item} } )
  0            
2275             {
2276 0           my $tmp_instance_status = $self->_create_describe_instance_status( $tmp_instancestatus_elem );
2277 0           push @$instancestatuses, $tmp_instance_status;
2278             }
2279 0 0         if ( grep { defined && length } $tmp_xml->{nextToken} ) {
  0 0          
2280 0           $token = $tmp_xml->{nextToken};
2281             }
2282             else {
2283 0           last;
2284             }
2285             }
2286             }
2287             }
2288             }
2289              
2290 0           return $instancestatuses;
2291             }
2292              
2293             =head2 _create_describe_instance_status(%instanceElement)
2294              
2295             Returns a blessed object. Used internally for wrapping describe_instance_status nextToken calls
2296              
2297             =over
2298              
2299             =item InstanceStatusElement (required)
2300              
2301             The instance status element we want to build out and return
2302              
2303             =back
2304              
2305             Returns a Net::Amazon::EC2::InstanceStatuses object
2306              
2307             =cut
2308              
2309             sub _create_describe_instance_status {
2310 0     0     my $self = shift;
2311 0           my $instancestatus_elem = shift;
2312              
2313 0           my $group_sets = [];
2314              
2315             my $instancestatus_state = Net::Amazon::EC2::InstanceState->new(
2316             code => $instancestatus_elem->{instanceState}{code},
2317             name => $instancestatus_elem->{instanceState}{name},
2318 0           );
2319              
2320 0           foreach
2321 0           my $events_arr ( @{ $instancestatus_elem->{eventsSet}{item} } )
2322             {
2323 0           my $events;
2324 0 0         if ( grep { defined && length } $events_arr->{notAfter} ) {
  0 0          
2325             $events = Net::Amazon::EC2::Events->new(
2326             code => $events_arr->{code},
2327             description => $events_arr->{description},
2328             not_before => $events_arr->{notBefore},
2329             not_after => $events_arr->{notAfter},
2330 0           );
2331             }
2332             else {
2333             $events = Net::Amazon::EC2::Events->new(
2334             code => $events_arr->{code},
2335             description => $events_arr->{description},
2336             not_before => $events_arr->{notBefore},
2337 0           );
2338             }
2339 0           push @$group_sets, $events;
2340             }
2341              
2342 0           my $instancestatus_istatus;
2343 0 0         if ( grep { defined && length }
  0 0          
2344             $instancestatus_elem->{instanceStatus} )
2345             {
2346 0           my $details_set = [];
2347 0           foreach my $details_arr (
2348 0           @{ $instancestatus_elem->{instanceStatus}{details}{item} } )
2349             {
2350             my $details = Net::Amazon::EC2::Details->new(
2351             status => $details_arr->{status},
2352             name => $details_arr->{name},
2353 0           );
2354 0           push @$details_set, $details;
2355             }
2356             $instancestatus_istatus =
2357             Net::Amazon::EC2::InstanceStatus->new(
2358             status => $instancestatus_elem->{instanceStatus}{status},
2359 0           details => $details_set,
2360             );
2361             }
2362              
2363 0           my $instancestatus_sstatus;
2364 0 0         if ( grep { defined && length }
  0 0          
2365             $instancestatus_elem->{systemStatus} )
2366             {
2367 0           my $details_set = [];
2368 0           foreach my $details_arr (
2369 0           @{ $instancestatus_elem->{systemStatus}{details}{item} } )
2370             {
2371             my $details = Net::Amazon::EC2::Details->new(
2372             status => $details_arr->{status},
2373             name => $details_arr->{name},
2374 0           );
2375 0           push @$details_set, $details;
2376             }
2377             $instancestatus_sstatus = Net::Amazon::EC2::SystemStatus->new(
2378             status => $instancestatus_elem->{systemStatus}{status},
2379 0           details => $details_set,
2380             );
2381             }
2382              
2383             my $instance_status = Net::Amazon::EC2::InstanceStatuses->new(
2384             availability_zone => $instancestatus_elem->{availabilityZone},
2385             events => $group_sets,
2386             instance_id => $instancestatus_elem->{instanceId},
2387 0           instance_state => $instancestatus_state,
2388             instance_status => $instancestatus_istatus,
2389             system_status => $instancestatus_sstatus,
2390              
2391             );
2392              
2393 0           return $instance_status;
2394             }
2395              
2396             =head2 describe_instance_attribute(%params)
2397              
2398             Returns information about an attribute of an instance. Only one attribute can be specified per call.
2399              
2400             =over
2401              
2402             =item InstanceId (required)
2403              
2404             The instance id we want to describe the attributes of.
2405              
2406             =item Attribute (required)
2407              
2408             The attribute we want to describe. Valid values are:
2409              
2410             =over
2411              
2412             =item * instanceType
2413              
2414             =item * kernel
2415              
2416             =item * ramdisk
2417              
2418             =item * userData
2419              
2420             =item * disableApiTermination
2421              
2422             =item * instanceInitiatedShutdownBehavior
2423              
2424             =item * rootDeviceName
2425              
2426             =item * blockDeviceMapping
2427              
2428             =back
2429              
2430             =back
2431              
2432             Returns a Net::Amazon::EC2::DescribeInstanceAttributeResponse object
2433              
2434             =cut
2435              
2436             sub describe_instance_attribute {
2437 0     0 1   my $self = shift;
2438 0           my %args = validate( @_, {
2439             InstanceId => { type => SCALAR },
2440             Attribute => { type => SCALAR },
2441             });
2442            
2443 0           my $xml = $self->_sign(Action => 'DescribeInstanceAttribute', %args);
2444              
2445 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2446 0           return $self->_parse_errors($xml);
2447             }
2448             else {
2449 0           my $attribute_response;
2450            
2451             # Test to see which type of attribute we are looking for, to dictacte
2452             # how to create the Net::Amazon::EC2::DescribeInstanceAttributeResponse object.
2453 0 0         if ( $args{Attribute} eq 'instanceType' ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2454             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2455             instance_id => $xml->{instanceId},
2456             instance_type => $xml->{instanceType}{value},
2457 0           );
2458             }
2459             elsif ( $args{Attribute} eq 'kernel' ) {
2460             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2461             instance_id => $xml->{instanceId},
2462             kernel => $xml->{kernel}{value},
2463 0           );
2464             }
2465             elsif ( $args{Attribute} eq 'ramdisk' ) {
2466             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2467             instance_id => $xml->{instanceId},
2468             ramdisk => $xml->{ramdisk}{value},
2469 0           );
2470             }
2471             elsif ( $args{Attribute} eq 'userData' ) {
2472             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2473             instance_id => $xml->{instanceId},
2474             user_data => $xml->{userData}{value},
2475 0           );
2476             }
2477             elsif ( $args{Attribute} eq 'disableApiTermination' ) {
2478             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2479             instance_id => $xml->{instanceId},
2480             disable_api_termination => $xml->{disableApiTermination}{value},
2481 0           );
2482             }
2483             elsif ( $args{Attribute} eq 'instanceInitiatedShutdownBehavior' ) {
2484             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2485             instance_id => $xml->{instanceId},
2486             instance_initiated_shutdown_behavior => $xml->{instanceInitiatedShutdownBehavior}{value},
2487 0           );
2488             }
2489             elsif ( $args{Attribute} eq 'rootDeviceName' ) {
2490             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2491             instance_id => $xml->{instanceId},
2492             root_device_name => $xml->{rootDeviceName}{value},
2493 0           );
2494             }
2495             elsif ( $args{Attribute} eq 'blockDeviceMapping' ) {
2496 0           my $block_mappings;
2497 0           foreach my $block_item (@{$xml->{blockDeviceMapping}{item}}) {
  0            
2498             my $ebs_mapping = Net::Amazon::EC2::EbsInstanceBlockDeviceMapping->new(
2499             attach_time => $block_item->{ebs}{attachTime},
2500             delete_on_termination => $block_item->{ebs}{deleteOnTermination},
2501             status => $block_item->{ebs}{status},
2502             volume_id => $block_item->{ebs}{volumeId},
2503 0           );
2504             my $block_device_mapping = Net::Amazon::EC2::BlockDeviceMapping->new(
2505             device_name => $block_item->{deviceName},
2506 0           ebs => $ebs_mapping,
2507             );
2508            
2509 0           push @$block_mappings, $block_device_mapping;
2510             }
2511              
2512             $attribute_response = Net::Amazon::EC2::DescribeInstanceAttributeResponse->new(
2513             instance_id => $xml->{instanceId},
2514 0           block_device_mapping => $block_mappings,
2515             );
2516             }
2517            
2518 0           return $attribute_response;
2519             }
2520             }
2521              
2522              
2523             =head2 describe_key_pairs(%params)
2524              
2525             This method describes the keypairs available on this account. It takes the following parameter:
2526              
2527             =over
2528              
2529             =item KeyName (optional)
2530              
2531             The name of the key to be described. Can be either a scalar or an array ref.
2532              
2533             =back
2534              
2535             Returns an array ref of Net::Amazon::EC2::DescribeKeyPairsResponse objects
2536              
2537             =cut
2538              
2539             sub describe_key_pairs {
2540 0     0 1   my $self = shift;
2541 0           my %args = validate( @_, {
2542             KeyName => { type => SCALAR | ARRAYREF, optional => 1 },
2543             });
2544            
2545             # If we have a array ref of instances lets split them out into their InstanceId.n format
2546 0 0         if (ref ($args{KeyName}) eq 'ARRAY') {
2547 0           my $keynames = delete $args{KeyName};
2548 0           my $count = 1;
2549 0           foreach my $keyname (@{$keynames}) {
  0            
2550 0           $args{"KeyName." . $count} = $keyname;
2551 0           $count++;
2552             }
2553             }
2554            
2555 0           my $xml = $self->_sign(Action => 'DescribeKeyPairs', %args);
2556              
2557 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2558 0           return $self->_parse_errors($xml);
2559             }
2560             else {
2561 0           my $key_pairs;
2562              
2563 0           foreach my $pair (@{$xml->{keySet}{item}}) {
  0            
2564             my $key_pair = Net::Amazon::EC2::DescribeKeyPairsResponse->new(
2565             key_name => $pair->{keyName},
2566             key_fingerprint => $pair->{keyFingerprint},
2567 0           );
2568            
2569 0           push @$key_pairs, $key_pair;
2570             }
2571              
2572 0           return $key_pairs;
2573             }
2574             }
2575              
2576             =head2 describe_regions(%params)
2577              
2578             Describes EC2 regions that are currently available to launch instances in for this account.
2579              
2580             =over
2581              
2582             =item RegionName (optional)
2583              
2584             The name of the region(s) to be described. Can be either a scalar or an array ref.
2585              
2586             =back
2587              
2588             Returns an array ref of Net::Amazon::EC2::Region objects
2589              
2590             =cut
2591              
2592             sub describe_regions {
2593 0     0 1   my $self = shift;
2594 0           my %args = validate( @_, {
2595             RegionName => { type => ARRAYREF | SCALAR, optional => 1 },
2596             });
2597              
2598             # If we have a array ref of regions lets split them out into their RegionName.n format
2599 0 0         if (ref ($args{RegionName}) eq 'ARRAY') {
2600 0           my $regions = delete $args{RegionName};
2601 0           my $count = 1;
2602 0           foreach my $region (@{$regions}) {
  0            
2603 0           $args{"RegionName." . $count} = $region;
2604 0           $count++;
2605             }
2606             }
2607            
2608 0           my $xml = $self->_sign(Action => 'DescribeRegions', %args);
2609            
2610 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2611 0           return $self->_parse_errors($xml);
2612             }
2613             else {
2614 0           my $regions;
2615              
2616 0           foreach my $region_item (@{$xml->{regionInfo}{item}}) {
  0            
2617             my $region = Net::Amazon::EC2::Region->new(
2618             region_name => $region_item->{regionName},
2619             region_endpoint => $region_item->{regionEndpoint},
2620 0           );
2621            
2622 0           push @$regions, $region;
2623             }
2624            
2625 0           return $regions;
2626             }
2627             }
2628              
2629             =head2 describe_reserved_instances(%params)
2630              
2631             Describes Reserved Instances that you purchased.
2632              
2633             =over
2634              
2635             =item ReservedInstancesId (optional)
2636              
2637             The reserved instance id(s) to be described. Can be either a scalar or an array ref.
2638              
2639             =back
2640              
2641             Returns an array ref of Net::Amazon::EC2::ReservedInstance objects
2642              
2643             =cut
2644              
2645             sub describe_reserved_instances {
2646 0     0 1   my $self = shift;
2647 0           my %args = validate( @_, {
2648             ReservedInstancesId => { type => ARRAYREF | SCALAR, optional => 1 },
2649             });
2650              
2651             # If we have a array ref of reserved instances lets split them out into their ReservedInstancesId.n format
2652 0 0         if (ref ($args{ReservedInstancesId}) eq 'ARRAY') {
2653 0           my $reserved_instance_ids = delete $args{ReservedInstancesId};
2654 0           my $count = 1;
2655 0           foreach my $reserved_instance_id (@{$reserved_instance_ids}) {
  0            
2656 0           $args{"ReservedInstancesId." . $count} = $reserved_instance_id;
2657 0           $count++;
2658             }
2659             }
2660            
2661 0           my $xml = $self->_sign(Action => 'DescribeReservedInstances', %args);
2662            
2663 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2664 0           return $self->_parse_errors($xml);
2665             }
2666             else {
2667 0           my $reserved_instances;
2668              
2669 0           foreach my $reserved_instance_item (@{$xml->{reservedInstancesSet}{item}}) {
  0            
2670             my $reserved_instance = Net::Amazon::EC2::ReservedInstance->new(
2671             reserved_instances_id => $reserved_instance_item->{reservedInstancesId},
2672             instance_type => $reserved_instance_item->{instanceType},
2673             availability_zone => $reserved_instance_item->{availabilityZone},
2674             duration => $reserved_instance_item->{duration},
2675             start => $reserved_instance_item->{start},
2676             usage_price => $reserved_instance_item->{usagePrice},
2677             fixed_price => $reserved_instance_item->{fixedPrice},
2678             instance_count => $reserved_instance_item->{instanceCount},
2679             product_description => $reserved_instance_item->{productDescription},
2680             state => $reserved_instance_item->{state},
2681 0           );
2682            
2683 0           push @$reserved_instances, $reserved_instance;
2684             }
2685            
2686 0           return $reserved_instances;
2687             }
2688             }
2689              
2690             =head2 describe_reserved_instances_offerings(%params)
2691              
2692             Describes Reserved Instance offerings that are available for purchase. With Amazon EC2 Reserved Instances,
2693             you purchase the right to launch Amazon EC2 instances for a period of time (without getting insufficient
2694             capacity errors) and pay a lower usage rate for the actual time used.
2695              
2696             =over
2697              
2698             =item ReservedInstancesOfferingId (optional)
2699              
2700             ID of the Reserved Instances to describe.
2701              
2702             =item InstanceType (optional)
2703              
2704             The instance type. The default is m1.small. Amazon frequently updates their instance types.
2705              
2706             See http://aws.amazon.com/ec2/instance-types
2707              
2708             =item AvailabilityZone (optional)
2709              
2710             The Availability Zone in which the Reserved Instance can be used.
2711              
2712             =item ProductDescription (optional)
2713              
2714             The Reserved Instance description.
2715              
2716             =back
2717              
2718             Returns an array ref of Net::Amazon::EC2::ReservedInstanceOffering objects
2719              
2720             =cut
2721              
2722             sub describe_reserved_instances_offerings {
2723 0     0 1   my $self = shift;
2724 0           my %args = validate( @_, {
2725             ReservedInstancesOfferingId => { type => SCALAR, optional => 1 },
2726             InstanceType => { type => SCALAR, optional => 1 },
2727             AvailabilityZone => { type => SCALAR, optional => 1 },
2728             ProductDescription => { type => SCALAR, optional => 1 },
2729             });
2730              
2731 0           my $xml = $self->_sign(Action => 'DescribeReservedInstancesOfferings', %args);
2732            
2733 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2734 0           return $self->_parse_errors($xml);
2735             }
2736             else {
2737 0           my $reserved_instance_offerings;
2738              
2739 0           foreach my $reserved_instance_offering_item (@{$xml->{reservedInstancesOfferingsSet}{item}}) {
  0            
2740             my $reserved_instance_offering = Net::Amazon::EC2::ReservedInstanceOffering->new(
2741             reserved_instances_offering_id => $reserved_instance_offering_item->{reservedInstancesOfferingId},
2742             instance_type => $reserved_instance_offering_item->{instanceType},
2743             availability_zone => $reserved_instance_offering_item->{availabilityZone},
2744             duration => $reserved_instance_offering_item->{duration},
2745             start => $reserved_instance_offering_item->{start},
2746             usage_price => $reserved_instance_offering_item->{usagePrice},
2747             fixed_price => $reserved_instance_offering_item->{fixedPrice},
2748             instance_count => $reserved_instance_offering_item->{instanceCount},
2749             product_description => $reserved_instance_offering_item->{productDescription},
2750             state => $reserved_instance_offering_item->{state},
2751 0           );
2752            
2753 0           push @$reserved_instance_offerings, $reserved_instance_offering;
2754             }
2755            
2756 0           return $reserved_instance_offerings;
2757             }
2758             }
2759              
2760             =head2 describe_security_groups(%params)
2761              
2762             This method describes the security groups available to this account. It takes the following parameter:
2763              
2764             =over
2765              
2766             =item GroupName (optional)
2767              
2768             The name of the security group(s) to be described. Can be either a scalar or an array ref.
2769              
2770             =item GroupId (optional)
2771              
2772             The id of the security group(s) to be described. Can be either a scalar or an array ref.
2773              
2774             =back
2775              
2776             Returns an array ref of Net::Amazon::EC2::SecurityGroup objects
2777              
2778             =cut
2779              
2780             sub describe_security_groups {
2781 0     0 1   my $self = shift;
2782 0           my %args = validate( @_, {
2783             GroupName => { type => SCALAR | ARRAYREF, optional => 1 },
2784             GroupId => { type => SCALAR | ARRAYREF, optional => 1 },
2785             });
2786              
2787             # If we have a array ref of GroupNames lets split them out into their GroupName.n format
2788 0 0         if (ref ($args{GroupName}) eq 'ARRAY') {
2789 0           my $groups = delete $args{GroupName};
2790 0           my $count = 1;
2791 0           foreach my $group (@{$groups}) {
  0            
2792 0           $args{"GroupName." . $count++} = $group;
2793             }
2794             }
2795            
2796             # If we have a array ref of GroupIds lets split them out into their GroupId.n format
2797 0 0         if (ref ($args{GroupId}) eq 'ARRAY') {
2798 0           my $groups = delete $args{GroupId};
2799 0           my $count = 1;
2800 0           foreach my $group (@{$groups}) {
  0            
2801 0           $args{"GroupId." . $count++} = $group;
2802             }
2803             }
2804              
2805 0           my $xml = $self->_sign(Action => 'DescribeSecurityGroups', %args);
2806            
2807 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2808 0           return $self->_parse_errors($xml);
2809             }
2810             else {
2811 0           my $security_groups;
2812 0           foreach my $sec_grp (@{$xml->{securityGroupInfo}{item}}) {
  0            
2813 0           my $owner_id = $sec_grp->{ownerId};
2814 0           my $group_name = $sec_grp->{groupName};
2815 0           my $group_id = $sec_grp->{groupId};
2816 0           my $group_description = $sec_grp->{groupDescription};
2817 0           my $vpc_id = $sec_grp->{vpcId};
2818 0           my $tag_set;
2819             my $ip_permissions;
2820 0           my $ip_permissions_egress;
2821              
2822 0           foreach my $ip_perm (@{$sec_grp->{ipPermissions}{item}}) {
  0            
2823 0           my $ip_protocol = $ip_perm->{ipProtocol};
2824 0           my $from_port = $ip_perm->{fromPort};
2825 0           my $to_port = $ip_perm->{toPort};
2826 0           my $icmp_port = $ip_perm->{icmpPort};
2827 0           my $groups;
2828             my $ip_ranges;
2829            
2830 0 0         if (grep { defined && length } $ip_perm->{groups}{item}) {
  0 0          
2831 0           foreach my $grp (@{$ip_perm->{groups}{item}}) {
  0            
2832             my $group = Net::Amazon::EC2::UserIdGroupPair->new(
2833             user_id => $grp->{userId},
2834             group_name => $grp->{groupName},
2835 0           );
2836            
2837 0           push @$groups, $group;
2838             }
2839             }
2840            
2841 0 0         if (grep { defined && length } $ip_perm->{ipRanges}{item}) {
  0 0          
2842 0           foreach my $rng (@{$ip_perm->{ipRanges}{item}}) {
  0            
2843             my $ip_range = Net::Amazon::EC2::IpRange->new(
2844             cidr_ip => $rng->{cidrIp},
2845 0           );
2846            
2847 0           push @$ip_ranges, $ip_range;
2848             }
2849             }
2850              
2851            
2852 0           my $ip_permission = Net::Amazon::EC2::IpPermission->new(
2853             ip_protocol => $ip_protocol,
2854             group_name => $group_name,
2855             group_description => $group_description,
2856             from_port => $from_port,
2857             to_port => $to_port,
2858             icmp_port => $icmp_port,
2859             );
2860            
2861 0 0         if ($ip_ranges) {
2862 0           $ip_permission->ip_ranges($ip_ranges);
2863             }
2864              
2865 0 0         if ($groups) {
2866 0           $ip_permission->groups($groups);
2867             }
2868            
2869 0           push @$ip_permissions, $ip_permission;
2870             }
2871            
2872 0           foreach my $ip_perm (@{$sec_grp->{ipPermissionsEgress}{item}}) {
  0            
2873 0           my $ip_protocol = $ip_perm->{ipProtocol};
2874 0           my $from_port = $ip_perm->{fromPort};
2875 0           my $to_port = $ip_perm->{toPort};
2876 0           my $icmp_port = $ip_perm->{icmpPort};
2877 0           my $groups;
2878             my $ip_ranges;
2879            
2880 0 0         if (grep { defined && length } $ip_perm->{groups}{item}) {
  0 0          
2881 0           foreach my $grp (@{$ip_perm->{groups}{item}}) {
  0            
2882             my $group = Net::Amazon::EC2::UserIdGroupPair->new(
2883             user_id => $grp->{userId},
2884             group_name => $grp->{groupName},
2885 0           );
2886            
2887 0           push @$groups, $group;
2888             }
2889             }
2890            
2891 0 0         if (grep { defined && length } $ip_perm->{ipRanges}{item}) {
  0 0          
2892 0           foreach my $rng (@{$ip_perm->{ipRanges}{item}}) {
  0            
2893             my $ip_range = Net::Amazon::EC2::IpRange->new(
2894             cidr_ip => $rng->{cidrIp},
2895 0           );
2896            
2897 0           push @$ip_ranges, $ip_range;
2898             }
2899             }
2900              
2901            
2902 0           my $ip_permission = Net::Amazon::EC2::IpPermission->new(
2903             ip_protocol => $ip_protocol,
2904             group_name => $group_name,
2905             group_description => $group_description,
2906             from_port => $from_port,
2907             to_port => $to_port,
2908             icmp_port => $icmp_port,
2909             );
2910            
2911 0 0         if ($ip_ranges) {
2912 0           $ip_permission->ip_ranges($ip_ranges);
2913             }
2914              
2915 0 0         if ($groups) {
2916 0           $ip_permission->groups($groups);
2917             }
2918            
2919 0           push @$ip_permissions_egress, $ip_permission;
2920             }
2921            
2922            
2923 0           foreach my $sec_tag (@{$sec_grp->{tagSet}{item}})
  0            
2924             {
2925             my $tag = Net::Amazon::EC2::TagSet->new(
2926             key => $sec_tag->{key},
2927             value => $sec_tag->{value},
2928 0           );
2929 0           push @$tag_set, $tag;
2930             }
2931              
2932 0           my $security_group = Net::Amazon::EC2::SecurityGroup->new(
2933             owner_id => $owner_id,
2934             group_name => $group_name,
2935             group_id => $group_id,
2936             vpc_id => $vpc_id,
2937             tag_set => $tag_set,
2938             group_description => $group_description,
2939             ip_permissions => $ip_permissions,
2940             ip_permissions_egress => $ip_permissions_egress,
2941             );
2942            
2943 0           push @$security_groups, $security_group;
2944             }
2945            
2946 0           return $security_groups;
2947             }
2948             }
2949              
2950             =head2 describe_snapshot_attribute(%params)
2951              
2952             Describes the snapshots attributes related to the snapshot in question. It takes the following arguments:
2953              
2954             =over
2955              
2956             =item SnapshotId (optional)
2957              
2958             Either a scalar or array ref of snapshot id's can be passed in. If this isn't passed in
2959             it will describe the attributes of all the current snapshots.
2960              
2961             =item Attribute (required)
2962              
2963             The attribute to describe, currently, the only valid attribute is createVolumePermission.
2964              
2965             =back
2966              
2967             Returns a Net::Amazon::EC2::SnapshotAttribute object.
2968              
2969             =cut
2970              
2971             sub describe_snapshot_attribute {
2972 0     0 1   my $self = shift;
2973 0           my %args = validate( @_, {
2974             SnapshotId => { type => ARRAYREF | SCALAR, optional => 1 },
2975             Attribute => { type => SCALAR },
2976             });
2977              
2978             # If we have a array ref of volumes lets split them out into their SnapshotId.n format
2979 0 0         if (ref ($args{SnapshotId}) eq 'ARRAY') {
2980 0           my $snapshots = delete $args{SnapshotId};
2981 0           my $count = 1;
2982 0           foreach my $snapshot (@{$snapshots}) {
  0            
2983 0           $args{"SnapshotId." . $count} = $snapshot;
2984 0           $count++;
2985             }
2986             }
2987            
2988 0           my $xml = $self->_sign(Action => 'DescribeSnapshotAttribute', %args);
2989            
2990 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
2991 0           return $self->_parse_errors($xml);
2992             }
2993             else {
2994 0           my $perms;
2995            
2996 0 0 0       unless ( grep { defined && length } $xml->{createVolumePermission} and ref $xml->{createVolumePermission} ne 'HASH') {
  0 0          
2997 0           $perms = undef;
2998             }
2999              
3000 0           foreach my $perm_item (@{$xml->{createVolumePermission}{item}}) {
  0            
3001             my $perm = Net::Amazon::EC2::CreateVolumePermission->new(
3002             user_id => $perm_item->{userId},
3003             group => $perm_item->{group},
3004 0           );
3005            
3006 0           push @$perms, $perm;
3007             }
3008              
3009             my $snapshot_attribute = Net::Amazon::EC2::SnapshotAttribute->new(
3010             snapshot_id => $xml->{snapshotId},
3011 0           permissions => $perms,
3012             );
3013            
3014 0           return $snapshot_attribute;
3015             }
3016             }
3017              
3018              
3019             =head2 describe_snapshots(%params)
3020              
3021             Describes the snapshots available to the user. It takes the following arguments:
3022              
3023             =over
3024              
3025             =item SnapshotId (optional)
3026              
3027             Either a scalar or array ref of snapshot id's can be passed in. If this isn't passed in
3028             it will describe all the current snapshots.
3029              
3030             =item Owner (optional)
3031              
3032             The owner of the snapshot.
3033              
3034             =item RestorableBy (optional)
3035              
3036             A user who can create volumes from the snapshot.
3037              
3038             =item Filter (optional)
3039              
3040             The filters for only the matching snapshots to be 'described'. A
3041             filter tuple is an arrayref constsing one key and one or more values.
3042             The option takes one filter tuple, or an arrayref of multiple filter
3043             tuples.
3044              
3045             =back
3046              
3047             Returns an array ref of Net::Amazon::EC2::Snapshot objects.
3048              
3049             =cut
3050              
3051             sub describe_snapshots {
3052 0     0 1   my $self = shift;
3053 0           my %args = validate( @_, {
3054             SnapshotId => { type => ARRAYREF | SCALAR, optional => 1 },
3055             Owner => { type => SCALAR, optional => 1 },
3056             RestorableBy => { type => SCALAR, optional => 1 },
3057             Filter => { type => ARRAYREF, optional => 1 },
3058             });
3059              
3060 0           $self->_build_filters(\%args);
3061              
3062             # If we have a array ref of volumes lets split them out into their SnapshotId.n format
3063 0 0         if (ref ($args{SnapshotId}) eq 'ARRAY') {
3064 0           my $snapshots = delete $args{SnapshotId};
3065 0           my $count = 1;
3066 0           foreach my $snapshot (@{$snapshots}) {
  0            
3067 0           $args{"SnapshotId." . $count} = $snapshot;
3068 0           $count++;
3069             }
3070             }
3071            
3072 0           my $xml = $self->_sign(Action => 'DescribeSnapshots', %args);
3073            
3074 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3075 0           return $self->_parse_errors($xml);
3076             }
3077             else {
3078 0           my $snapshots;
3079              
3080 0           foreach my $snap (@{$xml->{snapshotSet}{item}}) {
  0            
3081 0 0 0       unless ( grep { defined && length } $snap->{description} and ref $snap->{description} ne 'HASH') {
  0 0          
3082 0           $snap->{description} = undef;
3083             }
3084              
3085 0 0 0       unless ( grep { defined && length } $snap->{progress} and ref $snap->{progress} ne 'HASH') {
  0 0          
3086 0           $snap->{progress} = undef;
3087             }
3088              
3089 0           my $tag_sets;
3090 0           foreach my $tag_arr (@{$snap->{tagSet}{item}}) {
  0            
3091 0 0         if ( ref $tag_arr->{value} eq "HASH" ) {
3092 0           $tag_arr->{value} = "";
3093             }
3094             my $tag = Net::Amazon::EC2::TagSet->new(
3095             key => $tag_arr->{key},
3096             value => $tag_arr->{value},
3097 0           );
3098 0           push @$tag_sets, $tag;
3099             }
3100              
3101             my $snapshot = Net::Amazon::EC2::Snapshot->new(
3102             snapshot_id => $snap->{snapshotId},
3103             status => $snap->{status},
3104             volume_id => $snap->{volumeId},
3105             start_time => $snap->{startTime},
3106             progress => $snap->{progress},
3107             owner_id => $snap->{ownerId},
3108             volume_size => $snap->{volumeSize},
3109             description => $snap->{description},
3110             owner_alias => $snap->{ownerAlias},
3111 0           tag_set => $tag_sets,
3112             );
3113            
3114 0           push @$snapshots, $snapshot;
3115             }
3116            
3117 0           return $snapshots;
3118             }
3119             }
3120              
3121             =head2 describe_volumes(%params)
3122              
3123             Describes the volumes currently created. It takes the following arguments:
3124              
3125             =over
3126              
3127             =item VolumeId (optional)
3128              
3129             Either a scalar or array ref of volume id's can be passed in. If this isn't passed in
3130             it will describe all the current volumes.
3131              
3132             =back
3133              
3134             Returns an array ref of Net::Amazon::EC2::Volume objects.
3135              
3136             =cut
3137              
3138             sub describe_volumes {
3139 0     0 1   my $self = shift;
3140 0           my %args = validate( @_, {
3141             VolumeId => { type => ARRAYREF | SCALAR, optional => 1 },
3142             });
3143              
3144             # If we have a array ref of volumes lets split them out into their Volume.n format
3145 0 0         if (ref ($args{VolumeId}) eq 'ARRAY') {
3146 0           my $volumes = delete $args{VolumeId};
3147 0           my $count = 1;
3148 0           foreach my $volume (@{$volumes}) {
  0            
3149 0           $args{"VolumeId." . $count} = $volume;
3150 0           $count++;
3151             }
3152             }
3153            
3154 0           my $xml = $self->_sign(Action => 'DescribeVolumes', %args);
3155              
3156            
3157 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3158 0           return $self->_parse_errors($xml);
3159             }
3160             else {
3161 0           my $volumes;
3162              
3163 0           foreach my $volume_set (@{$xml->{volumeSet}{item}}) {
  0            
3164 0           my $attachments;
3165 0 0 0       unless ( grep { defined && length } $volume_set->{snapshotId} and ref $volume_set->{snapshotId} ne 'HASH') {
  0 0          
3166 0           $volume_set->{snapshotId} = undef;
3167             }
3168            
3169 0           foreach my $attachment_set (@{$volume_set->{attachmentSet}{item}}) {
  0            
3170             my $attachment = Net::Amazon::EC2::Attachment->new(
3171             volume_id => $attachment_set->{volumeId},
3172             status => $attachment_set->{status},
3173             instance_id => $attachment_set->{instanceId},
3174             attach_time => $attachment_set->{attachTime},
3175             device => $attachment_set->{device},
3176             delete_on_termination => $attachment_set->{deleteOnTermination},
3177 0           );
3178            
3179 0           push @$attachments, $attachment;
3180             }
3181            
3182 0           my $tags;
3183 0           foreach my $tag_arr (@{$volume_set->{tagSet}{item}}) {
  0            
3184 0 0         if ( ref $tag_arr->{value} eq "HASH" ) {
3185 0           $tag_arr->{value} = "";
3186             }
3187             my $tag = Net::Amazon::EC2::TagSet->new(
3188             key => $tag_arr->{key},
3189             value => $tag_arr->{value},
3190 0           );
3191 0           push @$tags, $tag;
3192             }
3193              
3194             my $volume = Net::Amazon::EC2::Volume->new(
3195             volume_id => $volume_set->{volumeId},
3196             status => $volume_set->{status},
3197             zone => $volume_set->{availabilityZone},
3198             create_time => $volume_set->{createTime},
3199             snapshot_id => $volume_set->{snapshotId},
3200             size => $volume_set->{size},
3201             volume_type => $volume_set->{volumeType},
3202             iops => $volume_set->{iops},
3203             encrypted => $volume_set->{encrypted},
3204 0           tag_set => $tags,
3205             attachments => $attachments,
3206             );
3207            
3208 0           push @$volumes, $volume;
3209             }
3210            
3211 0           return $volumes;
3212             }
3213             }
3214              
3215              
3216             =head2 describe_subnets(%params)
3217              
3218             This method describes the subnets on this account. It takes the following parameters:
3219              
3220             =over
3221              
3222             =item SubnetId (optional)
3223              
3224             The id of a subnet to be described. Can either be a scalar or an array ref.
3225              
3226             =item Filter.Name (optional)
3227              
3228             The name of the Filter.Name to be described. Can be either a scalar or an array ref.
3229             See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeSubnets.html
3230             for available filters.
3231              
3232             =item Filter.Value (optional)
3233              
3234             The name of the Filter.Value to be described. Can be either a scalar or an array ref.
3235              
3236             =back
3237              
3238             Returns an array ref of Net::Amazon::EC2::DescribeSubnetResponse objects
3239              
3240             =cut
3241              
3242             sub describe_subnets {
3243 0     0 1   my $self = shift;
3244 0           my %args = validate( @_, {
3245             'SubnetId' => { type => ARRAYREF | SCALAR, optional => 1 },
3246             'Filter.Name' => { type => ARRAYREF | SCALAR, optional => 1 },
3247             'Filter.Value' => { type => ARRAYREF | SCALAR, optional => 1 },
3248             });
3249              
3250 0 0         if (ref ($args{'SubnetId'}) eq 'ARRAY') {
3251 0           my $keys = delete $args{'SubnetId'};
3252 0           my $count = 1;
3253 0           foreach my $key (@{$keys}) {
  0            
3254 0           $args{"SubnetId." . $count } = $key;
3255 0           $count++;
3256             }
3257             }
3258 0 0         if (ref ($args{'Filter.Name'}) eq 'ARRAY') {
3259 0           my $keys = delete $args{'Filter.Name'};
3260 0           my $count = 1;
3261 0           foreach my $key (@{$keys}) {
  0            
3262 0           $args{"Filter." . $count . ".Name"} = $key;
3263 0           $count++;
3264             }
3265             }
3266 0 0         if (ref ($args{'Filter.Value'}) eq 'ARRAY') {
3267 0           my $keys = delete $args{'Filter.Value'};
3268 0           my $count = 1;
3269 0           foreach my $key (@{$keys}) {
  0            
3270 0           $args{"Filter." . $count . ".Value"} = $key;
3271 0           $count++;
3272             }
3273             }
3274              
3275 0           my $xml = $self->_sign(Action => 'DescribeSubnets', %args);
3276              
3277 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3278 0           return $self->_parse_errors($xml);
3279             }
3280             else {
3281 0           my $subnets;
3282              
3283 0           foreach my $pair (@{$xml->{subnetSet}{item}}) {
  0            
3284 0           my $tags;
3285              
3286 0           foreach my $tag_arr (@{$pair->{tagSet}{item}}) {
  0            
3287 0 0         if ( ref $tag_arr->{value} eq "HASH" ) {
3288 0           $tag_arr->{value} = "";
3289             }
3290             my $tag = Net::Amazon::EC2::TagSet->new(
3291             key => $tag_arr->{key},
3292             value => $tag_arr->{value},
3293 0           );
3294 0           push @$tags, $tag;
3295             }
3296              
3297             my $subnet = Net::Amazon::EC2::DescribeSubnetResponse->new(
3298             subnet_id => $pair->{subnetId},
3299             state => $pair->{state},
3300             vpc_id => $pair->{vpcId},
3301             cidr_block => $pair->{cidrBlock},
3302             available_ip_address_count => $pair->{availableIpAddressCount},
3303             availability_zone => $pair->{availabilityZone},
3304             default_for_az => $pair->{defaultForAz},
3305             map_public_ip_on_launch => $pair->{mapPublicIpOnLaunch},
3306 0           tag_set => $tags,
3307             );
3308              
3309 0           push @$subnets, $subnet;
3310             }
3311 0           return $subnets;
3312             }
3313             }
3314              
3315             =head2 describe_tags(%params)
3316              
3317             This method describes the tags available on this account. It takes the following parameter:
3318              
3319             =over
3320              
3321             =item Filter.Name (optional)
3322              
3323             The name of the Filter.Name to be described. Can be either a scalar or an array ref.
3324              
3325             =item Filter.Value (optional)
3326              
3327             The name of the Filter.Value to be described. Can be either a scalar or an array ref.
3328              
3329             =back
3330              
3331             Returns an array ref of Net::Amazon::EC2::DescribeTags objects
3332              
3333             =cut
3334              
3335             sub describe_tags {
3336 0     0 1   my $self = shift;
3337 0           my %args = validate( @_, {
3338             'Filter.Name' => { type => ARRAYREF | SCALAR, optional => 1 },
3339             'Filter.Value' => { type => ARRAYREF | SCALAR, optional => 1 },
3340             });
3341              
3342 0 0         if (ref ($args{'Filter.Name'}) eq 'ARRAY') {
3343 0           my $keys = delete $args{'Filter.Name'};
3344 0           my $count = 1;
3345 0           foreach my $key (@{$keys}) {
  0            
3346 0           $args{"Filter." . $count . ".Name"} = $key;
3347 0           $count++;
3348             }
3349             }
3350 0 0         if (ref ($args{'Filter.Value'}) eq 'ARRAY') {
3351 0           my $keys = delete $args{'Filter.Value'};
3352 0           my $count = 1;
3353 0           foreach my $key (@{$keys}) {
  0            
3354 0           $args{"Filter." . $count . ".Value"} = $key;
3355 0           $count++;
3356             }
3357             }
3358              
3359 0           my $xml = $self->_sign(Action => 'DescribeTags', %args);
3360              
3361 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3362 0           return $self->_parse_errors($xml);
3363             }
3364             else {
3365 0           my $tags;
3366              
3367 0           foreach my $pair (@{$xml->{tagSet}{item}}) {
  0            
3368             my $tag = Net::Amazon::EC2::DescribeTags->new(
3369             resource_id => $pair->{resourceId},
3370             resource_type => $pair->{resourceType},
3371             key => $pair->{key},
3372             value => $pair->{value},
3373 0           );
3374            
3375 0           push @$tags, $tag;
3376             }
3377              
3378 0           return $tags;
3379             }
3380             }
3381              
3382             =head2 detach_volume(%params)
3383              
3384             Detach a volume from an instance.
3385              
3386             =over
3387              
3388             =item VolumeId (required)
3389              
3390             The volume id you wish to detach.
3391              
3392             =item InstanceId (optional)
3393              
3394             The instance id you wish to detach from.
3395              
3396             =item Device (optional)
3397              
3398             The device the volume was attached as.
3399              
3400             =item Force (optional)
3401              
3402             A boolean for if to forcibly detach the volume from the instance.
3403             WARNING: This can lead to data loss or a corrupted file system.
3404             Use this option only as a last resort to detach a volume
3405             from a failed instance. The instance will not have an
3406             opportunity to flush file system caches nor file system
3407             meta data.
3408              
3409             =back
3410              
3411             Returns a Net::Amazon::EC2::Attachment object containing the resulting volume status.
3412              
3413             =cut
3414              
3415             sub detach_volume {
3416 0     0 1   my $self = shift;
3417 0           my %args = validate( @_, {
3418             VolumeId => { type => SCALAR },
3419             InstanceId => { type => SCALAR, optional => 1 },
3420             Device => { type => SCALAR, optional => 1 },
3421             Force => { type => SCALAR, optional => 1 },
3422             });
3423              
3424 0           my $xml = $self->_sign(Action => 'DetachVolume', %args);
3425              
3426            
3427 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3428 0           return $self->_parse_errors($xml);
3429             }
3430             else {
3431             my $attachment = Net::Amazon::EC2::Attachment->new(
3432             volume_id => $xml->{volumeId},
3433             status => $xml->{status},
3434             instance_id => $xml->{instanceId},
3435             attach_time => $xml->{attachTime},
3436             device => $xml->{device},
3437 0           );
3438            
3439 0           return $attachment;
3440             }
3441             }
3442              
3443             =head2 disassociate_address(%params)
3444              
3445             Disassociates an elastic IP address with an instance. It takes the following arguments:
3446              
3447             =over
3448              
3449             =item PublicIp (required)
3450              
3451             The IP address to disassociate
3452              
3453             =back
3454              
3455             Returns true if the disassociation succeeded.
3456              
3457             =cut
3458              
3459             sub disassociate_address {
3460 0     0 1   my $self = shift;
3461 0           my %args = validate( @_, {
3462             PublicIp => { type => SCALAR },
3463             });
3464            
3465 0           my $xml = $self->_sign(Action => 'DisassociateAddress', %args);
3466              
3467 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3468 0           return $self->_parse_errors($xml);
3469             }
3470             else {
3471 0 0         if ($xml->{return} eq 'true') {
3472 0           return 1;
3473             }
3474             else {
3475 0           return undef;
3476             }
3477             }
3478             }
3479              
3480             =head2 get_console_output(%params)
3481              
3482             This method gets the output from the virtual console for an instance. It takes the following parameters:
3483              
3484             =over
3485              
3486             =item InstanceId (required)
3487              
3488             A scalar containing a instance id.
3489              
3490             =back
3491              
3492             Returns a Net::Amazon::EC2::ConsoleOutput object or C<undef> if there is no
3493             new output. (This can happen in cases where the console output has not changed
3494             since the last call.)
3495              
3496             =cut
3497              
3498             sub get_console_output {
3499 0     0 1   my $self = shift;
3500 0           my %args = validate( @_, {
3501             InstanceId => { type => SCALAR },
3502             });
3503            
3504            
3505 0           my $xml = $self->_sign(Action => 'GetConsoleOutput', %args);
3506            
3507 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3508 0           return $self->_parse_errors($xml);
3509             }
3510             else {
3511 0 0         if ( grep { defined && length } $xml->{output} ) {
  0 0          
3512             my $console_output = Net::Amazon::EC2::ConsoleOutput->new(
3513             instance_id => $xml->{instanceId},
3514             timestamp => $xml->{timestamp},
3515 0           output => decode_base64($xml->{output}),
3516             );
3517 0           return $console_output;
3518             }
3519             else {
3520 0           return undef;
3521             }
3522             }
3523             }
3524              
3525             =head2 get_password_data(%params)
3526              
3527             Retrieves the encrypted administrator password for the instances running Windows. This procedure is not applicable for Linux and UNIX instances.
3528              
3529             =over
3530              
3531             =item InstanceId (required)
3532              
3533             The Instance Id for which to retrieve the password.
3534              
3535             =back
3536              
3537             Returns a Net::Amazon::EC2::InstancePassword object
3538              
3539             =cut
3540              
3541             sub get_password_data {
3542 0     0 1   my $self = shift;
3543 0           my %args = validate( @_, {
3544             instanceId => { type => SCALAR },
3545             });
3546              
3547 0           my $xml = $self->_sign(Action => 'GetPasswordData', %args);
3548            
3549 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3550 0           return $self->_parse_errors($xml);
3551             }
3552             else {
3553             my $instance_password = Net::Amazon::EC2::InstancePassword->new(
3554             instance_id => $xml->{instanceId},
3555             timestamp => $xml->{timestamp},
3556             password_data => $xml->{passwordData},
3557 0           );
3558            
3559 0           return $instance_password;
3560             }
3561             }
3562              
3563             =head2 modify_image_attribute(%params)
3564              
3565             This method modifies attributes of an machine image.
3566              
3567             =over
3568              
3569             =item ImageId (required)
3570              
3571             The AMI to modify the attributes of.
3572              
3573             =item Attribute (required)
3574              
3575             The attribute you wish to modify, right now the attributes you can modify are launchPermission and productCodes
3576              
3577             =item OperationType (required for launchPermission)
3578              
3579             The operation you wish to perform on the attribute. Right now just 'add' and 'remove' are supported.
3580              
3581             =item UserId (required for launchPermission)
3582              
3583             User Id's you wish to add/remove from the attribute.
3584              
3585             =item UserGroup (required for launchPermission)
3586              
3587             Groups you wish to add/remove from the attribute. Currently there is only one User Group available 'all' for all Amazon EC2 customers.
3588              
3589             =item ProductCode (required for productCodes)
3590              
3591             Attaches a product code to the AMI. Currently only one product code can be assigned to the AMI. Once this is set it cannot be changed or reset.
3592              
3593             =back
3594              
3595             Returns 1 if the modification succeeds.
3596              
3597             =cut
3598              
3599             sub modify_image_attribute {
3600 0     0 1   my $self = shift;
3601 0           my %args = validate( @_, {
3602             ImageId => { type => SCALAR },
3603             Attribute => { type => SCALAR },
3604             OperationType => { type => SCALAR, optional => 1 },
3605             UserId => { type => SCALAR | ARRAYREF, optional => 1 },
3606             UserGroup => { type => SCALAR | ARRAYREF, optional => 1 },
3607             ProductCode => { type => SCALAR, optional => 1 },
3608             });
3609            
3610            
3611 0           my $xml = $self->_sign(Action => 'ModifyImageAttribute', %args);
3612            
3613 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3614 0           return $self->_parse_errors($xml);
3615             }
3616             else {
3617 0 0         if ($xml->{return} eq 'true') {
3618 0           return 1;
3619             }
3620             else {
3621 0           return undef;
3622             }
3623             }
3624             }
3625              
3626             =head2 modify_instance_attribute(%params)
3627              
3628             Modify an attribute of an instance.
3629              
3630             =over
3631              
3632             =item InstanceId (required)
3633              
3634             The instance id we want to modify the attributes of.
3635              
3636             =item Attribute (required)
3637              
3638             The attribute we want to modify. Valid values are:
3639              
3640             =over
3641              
3642             =item * instanceType
3643              
3644             =item * kernel
3645              
3646             =item * ramdisk
3647              
3648             =item * userData
3649              
3650             =item * disableApiTermination
3651              
3652             =item * instanceInitiatedShutdownBehavior
3653              
3654             =item * rootDeviceName
3655              
3656             =item * blockDeviceMapping
3657              
3658             =back
3659              
3660             =item Value (required)
3661              
3662             The value to set the attribute to.
3663              
3664             You may also pass a hashref with one or more keys
3665             and values. This hashref will be flattened and
3666             passed to AWS.
3667              
3668             For example:
3669              
3670             $ec2->modify_instance_attribute(
3671             'InstanceId' => $id,
3672             'Attribute' => 'blockDeviceMapping',
3673             'Value' => {
3674             'BlockDeviceMapping.1.DeviceName' => '/dev/sdf1',
3675             'BlockDeviceMapping.1.Ebs.DeleteOnTermination' => 'true',
3676             }
3677             );
3678              
3679             =back
3680              
3681             Returns 1 if the modification succeeds.
3682              
3683             =cut
3684              
3685             sub modify_instance_attribute {
3686 0     0 1   my $self = shift;
3687 0           my %args = validate( @_, {
3688             InstanceId => { type => SCALAR },
3689             Attribute => { type => SCALAR },
3690             Value => { type => SCALAR | HASHREF },
3691             });
3692              
3693 0 0         if ( ref($args{'Value'}) eq "HASH" ) {
3694             # remove the 'Value' key and flatten the hashref
3695 0           my $href = delete $args{'Value'};
3696 0           map { $args{$_} = $href->{$_} } keys %{$href};
  0            
  0            
3697             }
3698            
3699 0           my $xml = $self->_sign(Action => 'ModifyInstanceAttribute', %args);
3700              
3701 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3702 0           return $self->_parse_errors($xml);
3703             }
3704             else {
3705 0 0         if ($xml->{return} eq 'true') {
3706 0           return 1;
3707             }
3708             else {
3709 0           return undef;
3710             }
3711             }
3712             }
3713              
3714              
3715             =head2 modify_snapshot_attribute(%params)
3716              
3717             This method modifies attributes of a snapshot.
3718              
3719             =over
3720              
3721             =item SnapshotId (required)
3722              
3723             The snapshot id to modify the attributes of.
3724              
3725             =item UserId (optional)
3726              
3727             User Id you wish to add/remove create volume permissions for.
3728              
3729             =item UserGroup (optional)
3730              
3731             User Id you wish to add/remove create volume permissions for. To make the snapshot createable by all
3732             set the UserGroup to "all".
3733              
3734             =item Attribute (required)
3735              
3736             The attribute you wish to modify, right now the only attribute you can modify is "CreateVolumePermission"
3737              
3738             =item OperationType (required)
3739              
3740             The operation you wish to perform on the attribute. Right now just 'add' and 'remove' are supported.
3741              
3742             =back
3743              
3744             Returns 1 if the modification succeeds.
3745              
3746             =cut
3747              
3748             sub modify_snapshot_attribute {
3749 0     0 1   my $self = shift;
3750 0           my %args = validate( @_, {
3751             SnapshotId => { type => SCALAR },
3752             UserId => { type => SCALAR, optional => 1 },
3753             UserGroup => { type => SCALAR, optional => 1 },
3754             Attribute => { type => SCALAR },
3755             OperationType => { type => SCALAR },
3756             });
3757            
3758            
3759 0           my $xml = $self->_sign(Action => 'ModifySnapshotAttribute', %args);
3760            
3761 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3762 0           return $self->_parse_errors($xml);
3763             }
3764             else {
3765 0 0         if ($xml->{return} eq 'true') {
3766 0           return 1;
3767             }
3768             else {
3769 0           return undef;
3770             }
3771             }
3772             }
3773              
3774             =head2 monitor_instances(%params)
3775              
3776             Enables monitoring for a running instance. For more information, refer to the Amazon CloudWatch Developer Guide.
3777              
3778             =over
3779              
3780             =item InstanceId (required)
3781              
3782             The instance id(s) to monitor. Can be a scalar or an array ref
3783              
3784             =back
3785              
3786             Returns an array ref of Net::Amazon::EC2::MonitoredInstance objects
3787              
3788             =cut
3789              
3790             sub monitor_instances {
3791 0     0 1   my $self = shift;
3792 0           my %args = validate( @_, {
3793             InstanceId => { type => ARRAYREF | SCALAR, optional => 1 },
3794             });
3795              
3796             # If we have a array ref of instances lets split them out into their InstanceId.n format
3797 0 0         if (ref ($args{InstanceId}) eq 'ARRAY') {
3798 0           my $instance_ids = delete $args{InstanceId};
3799 0           my $count = 1;
3800 0           foreach my $instance_id (@{$instance_ids}) {
  0            
3801 0           $args{"InstanceId." . $count} = $instance_id;
3802 0           $count++;
3803             }
3804             }
3805            
3806 0           my $xml = $self->_sign(Action => 'MonitorInstances', %args);
3807            
3808 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3809 0           return $self->_parse_errors($xml);
3810             }
3811             else {
3812 0           my $monitored_instances;
3813              
3814 0           foreach my $monitored_instance_item (@{$xml->{instancesSet}{item}}) {
  0            
3815             my $monitored_instance = Net::Amazon::EC2::ReservedInstance->new(
3816             instance_id => $monitored_instance_item->{instanceId},
3817             state => $monitored_instance_item->{monitoring}{state},
3818 0           );
3819            
3820 0           push @$monitored_instances, $monitored_instance;
3821             }
3822            
3823 0           return $monitored_instances;
3824             }
3825             }
3826              
3827             =head2 purchase_reserved_instances_offering(%params)
3828              
3829             Purchases a Reserved Instance for use with your account. With Amazon EC2 Reserved Instances, you purchase the right to
3830             launch Amazon EC2 instances for a period of time (without getting insufficient capacity errors) and pay a lower usage
3831             rate for the actual time used.
3832              
3833             =over
3834              
3835             =item ReservedInstancesOfferingId (required)
3836              
3837             ID of the Reserved Instances to describe. Can be either a scalar or an array ref.
3838              
3839             =item InstanceCount (optional)
3840              
3841             The number of Reserved Instances to purchase (default is 1). Can be either a scalar or an array ref.
3842              
3843             NOTE NOTE NOTE, the array ref needs to line up with the InstanceCount if you want to pass that in, so that
3844             the right number of instances are started of the right instance offering
3845              
3846             =back
3847              
3848             Returns 1 if the reservations succeeded.
3849              
3850             =cut
3851              
3852             sub purchase_reserved_instances_offering {
3853 0     0 1   my $self = shift;
3854 0           my %args = validate( @_, {
3855             ReservedInstancesOfferingId => { type => ARRAYREF | SCALAR },
3856             InstanceCount => { type => ARRAYREF | SCALAR, optional => 1 },
3857             });
3858            
3859             # If we have a array ref of reserved instance offerings lets split them out into their ReservedInstancesOfferingId.n format
3860 0 0         if (ref ($args{ReservedInstancesOfferingId}) eq 'ARRAY') {
3861 0           my $reserved_instance_offering_ids = delete $args{ReservedInstancesOfferingId};
3862 0           my $count = 1;
3863 0           foreach my $reserved_instance_offering_id (@{$reserved_instance_offering_ids}) {
  0            
3864 0           $args{"ReservedInstancesOfferingId." . $count} = $reserved_instance_offering_id;
3865 0           $count++;
3866             }
3867             }
3868              
3869             # If we have a array ref of instance counts lets split them out into their InstanceCount.n format
3870 0 0         if (ref ($args{InstanceCount}) eq 'ARRAY') {
3871 0           my $instance_counts = delete $args{InstanceCount};
3872 0           my $count = 1;
3873 0           foreach my $instance_count (@{$instance_counts}) {
  0            
3874 0           $args{"InstanceCount." . $count} = $instance_count;
3875 0           $count++;
3876             }
3877             }
3878            
3879 0           my $xml = $self->_sign(Action => 'PurchaseReservedInstancesOffering', %args);
3880            
3881 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3882 0           return $self->_parse_errors($xml);
3883             }
3884             else {
3885 0 0         if ($xml->{reservedInstancesId} ) {
3886 0           return 1;
3887             }
3888             else {
3889 0           return undef;
3890             }
3891             }
3892             }
3893              
3894             =head2 reboot_instances(%params)
3895              
3896             This method reboots an instance. It takes the following parameters:
3897              
3898             =over
3899              
3900             =item InstanceId (required)
3901              
3902             Instance Id of the instance you wish to reboot. Can be either a scalar or array ref of instances to reboot.
3903              
3904             =back
3905              
3906             Returns 1 if the reboot succeeded.
3907              
3908             =cut
3909              
3910             sub reboot_instances {
3911 0     0 1   my $self = shift;
3912 0           my %args = validate( @_, {
3913             InstanceId => { type => SCALAR | ARRAYREF },
3914             });
3915            
3916             # If we have a array ref of instances lets split them out into their InstanceId.n format
3917 0 0         if (ref ($args{InstanceId}) eq 'ARRAY') {
3918 0           my $instance_ids = delete $args{InstanceId};
3919 0           my $count = 1;
3920 0           foreach my $instance_id (@{$instance_ids}) {
  0            
3921 0           $args{"InstanceId." . $count} = $instance_id;
3922 0           $count++;
3923             }
3924             }
3925            
3926 0           my $xml = $self->_sign(Action => 'RebootInstances', %args);
3927            
3928 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
3929 0           return $self->_parse_errors($xml);
3930             }
3931             else {
3932 0 0         if ($xml->{return} eq 'true') {
3933 0           return 1;
3934             }
3935             else {
3936 0           return undef;
3937             }
3938             }
3939             }
3940              
3941             =head2 register_image(%params)
3942              
3943             This method registers an AMI on the EC2. It takes the following parameter:
3944              
3945             =over
3946              
3947             =item ImageLocation (optional)
3948              
3949             The location of the AMI manifest on S3
3950              
3951             =item Name (required)
3952              
3953             The name of the AMI that was provided during image creation.
3954              
3955             =item Description (optional)
3956              
3957             The description of the AMI.
3958              
3959             =item Architecture (optional)
3960              
3961             The architecture of the image. Either i386 or x86_64
3962              
3963             =item KernelId (optional)
3964              
3965             The ID of the kernel to select.
3966              
3967             =item RamdiskId (optional)
3968              
3969             The ID of the RAM disk to select. Some kernels require additional drivers at launch.
3970              
3971             =item RootDeviceName (optional)
3972              
3973             The root device name (e.g., /dev/sda1).
3974              
3975             =item BlockDeviceMapping (optional)
3976              
3977             This needs to be a data structure like this:
3978              
3979             [
3980             {
3981             deviceName => "/dev/sdh", (optional)
3982             virtualName => "ephemerel0", (optional)
3983             noDevice => "/dev/sdl", (optional),
3984             ebs => {
3985             snapshotId => "snap-0000", (optional)
3986             volumeSize => "20", (optional)
3987             deleteOnTermination => "false", (optional)
3988             },
3989             },
3990             ...
3991             ]
3992              
3993             =back
3994              
3995             Returns the image id of the new image on EC2.
3996              
3997             =cut
3998              
3999             sub register_image {
4000 0     0 1   my $self = shift;
4001 0           my %args = validate( @_, {
4002             ImageLocation => { type => SCALAR, optional => 1 },
4003             Name => { type => SCALAR },
4004             Description => { type => SCALAR, optional => 1 },
4005             Architecture => { type => SCALAR, optional => 1 },
4006             KernelId => { type => SCALAR, optional => 1 },
4007             RamdiskId => { type => SCALAR, optional => 1 },
4008             RootDeviceName => { type => SCALAR, optional => 1 },
4009             BlockDeviceMapping => { type => ARRAYREF, optional => 1 },
4010             });
4011              
4012            
4013             # If we have a array ref of block devices, we need to split them up
4014 0 0         if (ref ($args{BlockDeviceMapping}) eq 'ARRAY') {
4015 0           my $block_devices = delete $args{BlockDeviceMapping};
4016 0           my $count = 1;
4017 0           foreach my $block_device (@{$block_devices}) {
  0            
4018 0 0         $args{"BlockDeviceMapping." . $count . ".DeviceName"} = $block_device->{deviceName} if $block_device->{deviceName};
4019 0 0         $args{"BlockDeviceMapping." . $count . ".VirtualName"} = $block_device->{virtualName} if $block_device->{virtualName};
4020 0 0         $args{"BlockDeviceMapping." . $count . ".NoDevice"} = $block_device->{noDevice} if $block_device->{noDevice};
4021 0 0         $args{"BlockDeviceMapping." . $count . ".Ebs.SnapshotId"} = $block_device->{ebs}{snapshotId} if $block_device->{ebs}{snapshotId};
4022 0 0         $args{"BlockDeviceMapping." . $count . ".Ebs.VolumeSize"} = $block_device->{ebs}{volumeSize} if $block_device->{ebs}{volumeSize};
4023 0 0         $args{"BlockDeviceMapping." . $count . ".Ebs.DeleteOnTermination"} = $block_device->{ebs}{deleteOnTermination} if $block_device->{ebs}{deleteOnTermination};
4024 0           $count++;
4025             }
4026             }
4027              
4028 0           my $xml = $self->_sign(Action => 'RegisterImage', %args);
4029              
4030 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4031 0           return $self->_parse_errors($xml);
4032             }
4033             else {
4034 0           return $xml->{imageId};
4035             }
4036             }
4037              
4038             =head2 release_address(%params)
4039              
4040             Releases an allocated IP address. It takes the following arguments:
4041              
4042             =over
4043              
4044             =item PublicIp (required)
4045              
4046             The IP address to release
4047              
4048             =back
4049              
4050             Returns true if the releasing succeeded.
4051              
4052             =cut
4053              
4054             sub release_address {
4055 0     0 1   my $self = shift;
4056 0           my %args = validate( @_, {
4057             PublicIp => { type => SCALAR },
4058             });
4059            
4060 0           my $xml = $self->_sign(Action => 'ReleaseAddress', %args);
4061              
4062 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4063 0           return $self->_parse_errors($xml);
4064             }
4065             else {
4066 0 0         if ($xml->{return} eq 'true') {
4067 0           return 1;
4068             }
4069             else {
4070 0           return undef;
4071             }
4072             }
4073             }
4074              
4075             sub release_vpc_address {
4076 0     0 0   my $self = shift;
4077 0           my %args = validate( @_, {
4078             AllocationId => { type => SCALAR },
4079             });
4080              
4081 0           my $xml = $self->_sign(Action => 'ReleaseAddress', %args);
4082              
4083 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4084 0           return $self->_parse_errors($xml);
4085             }
4086             else {
4087 0 0         if ($xml->{return} eq 'true') {
4088 0           return 1;
4089             }
4090             else {
4091 0           return undef;
4092             }
4093             }
4094             }
4095              
4096             =head2 reset_image_attribute(%params)
4097              
4098             This method resets an attribute for an AMI to its default state (NOTE: product codes cannot be reset).
4099             It takes the following parameters:
4100              
4101             =over
4102              
4103             =item ImageId (required)
4104              
4105             The image id of the AMI you wish to reset the attributes on.
4106              
4107             =item Attribute (required)
4108              
4109             The attribute you want to reset.
4110              
4111             =back
4112              
4113             Returns 1 if the attribute reset succeeds.
4114              
4115             =cut
4116              
4117             sub reset_image_attribute {
4118 0     0 1   my $self = shift;
4119 0           my %args = validate( @_, {
4120             ImageId => { type => SCALAR },
4121             Attribute => { type => SCALAR },
4122             });
4123            
4124 0           my $xml = $self->_sign(Action => 'ResetImageAttribute', %args);
4125              
4126 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4127 0           return $self->_parse_errors($xml);
4128             }
4129             else {
4130 0 0         if ($xml->{return} eq 'true') {
4131 0           return 1;
4132             }
4133             else {
4134 0           return undef;
4135             }
4136             }
4137             }
4138              
4139             =head2 reset_instance_attribute(%params)
4140              
4141             Reset an attribute of an instance. Only one attribute can be specified per call.
4142              
4143             =over
4144              
4145             =item InstanceId (required)
4146              
4147             The instance id we want to reset the attributes of.
4148              
4149             =item Attribute (required)
4150              
4151             The attribute we want to reset. Valid values are:
4152              
4153             =over
4154              
4155             =item * kernel
4156              
4157             =item * ramdisk
4158              
4159             =back
4160              
4161             =back
4162              
4163             Returns 1 if the reset succeeds.
4164              
4165             =cut
4166              
4167             sub reset_instance_attribute {
4168 0     0 1   my $self = shift;
4169 0           my %args = validate( @_, {
4170             InstanceId => { type => SCALAR },
4171             Attribute => { type => SCALAR },
4172             });
4173            
4174 0           my $xml = $self->_sign(Action => 'ResetInstanceAttribute', %args);
4175              
4176 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4177 0           return $self->_parse_errors($xml);
4178             }
4179             else {
4180 0 0         if ($xml->{return} eq 'true') {
4181 0           return 1;
4182             }
4183             else {
4184 0           return undef;
4185             }
4186             }
4187             }
4188              
4189             =head2 reset_snapshot_attribute(%params)
4190              
4191             This method resets an attribute for an snapshot to its default state.
4192              
4193             It takes the following parameters:
4194              
4195             =over
4196              
4197             =item SnapshotId (required)
4198              
4199             The snapshot id of the snapshot you wish to reset the attributes on.
4200              
4201             =item Attribute (required)
4202              
4203             The attribute you want to reset (currently "CreateVolumePermission" is the only
4204             valid attribute).
4205              
4206             =back
4207              
4208             Returns 1 if the attribute reset succeeds.
4209              
4210             =cut
4211              
4212             sub reset_snapshot_attribute {
4213 0     0 1   my $self = shift;
4214 0           my %args = validate( @_, {
4215             SnapshotId => { type => SCALAR },
4216             Attribute => { type => SCALAR },
4217             });
4218            
4219 0           my $xml = $self->_sign(Action => 'ResetSnapshotAttribute', %args);
4220              
4221 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4222 0           return $self->_parse_errors($xml);
4223             }
4224             else {
4225 0 0         if ($xml->{return} eq 'true') {
4226 0           return 1;
4227             }
4228             else {
4229 0           return undef;
4230             }
4231             }
4232             }
4233              
4234             =head2 revoke_security_group_ingress(%params)
4235              
4236             This method revoke permissions to a security group. It takes the following parameters:
4237              
4238             =over
4239              
4240             =item GroupName (required)
4241              
4242             The name of the group to revoke security rules from.
4243              
4244             =item SourceSecurityGroupName (required when revoking a user and group together)
4245              
4246             Name of the group to revoke access from.
4247              
4248             =item SourceSecurityGroupOwnerId (required when revoking a user and group together)
4249              
4250             Owner of the group to revoke access from.
4251              
4252             =item IpProtocol (required when revoking access from a CIDR)
4253              
4254             IP Protocol of the rule you are revoking access from (TCP, UDP, or ICMP)
4255              
4256             =item FromPort (required when revoking access from a CIDR)
4257              
4258             Beginning of port range to revoke access from.
4259              
4260             =item ToPort (required when revoking access from a CIDR)
4261              
4262             End of port range to revoke access from.
4263              
4264             =item CidrIp (required when revoking access from a CIDR)
4265              
4266             The CIDR IP space we are revoking access from.
4267              
4268             =back
4269              
4270             Revoking a rule can be done in two ways: revoking a source group name + source group owner id, or, by Protocol + start port + end port + CIDR IP. The two are mutally exclusive.
4271              
4272             Returns 1 if rule is revoked successfully.
4273              
4274             =cut
4275              
4276             sub revoke_security_group_ingress {
4277 0     0 1   my $self = shift;
4278 0           my %args = validate( @_, {
4279             GroupName => { type => SCALAR, optional => 1 },
4280             GroupId => { type => SCALAR, optional => 1 },
4281             SourceSecurityGroupName => {
4282             type => SCALAR,
4283             depends => ['SourceSecurityGroupOwnerId'],
4284             optional => 1 ,
4285             },
4286             SourceSecurityGroupOwnerId => { type => SCALAR, optional => 1 },
4287             IpProtocol => {
4288             type => SCALAR,
4289             depends => ['FromPort', 'ToPort', 'CidrIp'],
4290             optional => 1
4291             },
4292             FromPort => { type => SCALAR, optional => 1 },
4293             ToPort => { type => SCALAR, optional => 1 },
4294             CidrIp => { type => SCALAR, optional => 1 },
4295             });
4296            
4297            
4298 0           my $xml = $self->_sign(Action => 'RevokeSecurityGroupIngress', %args);
4299              
4300 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4301 0           return $self->_parse_errors($xml);
4302             }
4303             else {
4304 0 0         if ($xml->{return} eq 'true') {
4305 0           return 1;
4306             }
4307             else {
4308 0           return undef;
4309             }
4310             }
4311             }
4312              
4313             =head2 run_instances(%params)
4314              
4315             This method will start instance(s) of AMIs on EC2. The parameters
4316             indicate which AMI to instantiate and how many / what properties they
4317             have:
4318              
4319             =over
4320              
4321             =item ImageId (required)
4322              
4323             The image id you want to start an instance of.
4324              
4325             =item MinCount (required)
4326              
4327             The minimum number of instances to start.
4328              
4329             =item MaxCount (required)
4330              
4331             The maximum number of instances to start.
4332              
4333             =item KeyName (optional)
4334              
4335             The keypair name to associate this instance with. If omitted, will use your default keypair.
4336              
4337             =item SecurityGroup (optional)
4338              
4339             An scalar or array ref. Will associate this instance with the group names passed in. If omitted, will be associated with the default security group.
4340              
4341             =item SecurityGroupId (optional)
4342              
4343             An scalar or array ref. Will associate this instance with the group ids passed in. If omitted, will be associated with the default security group.
4344              
4345             =item AdditionalInfo (optional)
4346              
4347             Specifies additional information to make available to the instance(s).
4348              
4349             =item UserData (optional)
4350              
4351             Optional data to pass into the instance being started. Needs to be base64 encoded.
4352              
4353             =item InstanceType (optional)
4354              
4355             Specifies the type of instance to start.
4356              
4357             See http://aws.amazon.com/ec2/instance-types
4358              
4359             The options are:
4360              
4361             =over
4362              
4363             =item m1.small (default)
4364              
4365             1 EC2 Compute Unit (1 virtual core with 1 EC2 Compute Unit). 32-bit or 64-bit, 1.7GB RAM, 160GB disk
4366              
4367             =item m1.medium Medium Instance
4368              
4369             2 EC2 Compute Units (1 virtual core with 2 EC2 Compute Unit), 32-bit or 64-bit, 3.75GB RAM, 410GB disk
4370              
4371             =item m1.large: Standard Large Instance
4372              
4373             4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each). 64-bit, 7.5GB RAM, 850GB disk
4374              
4375             =item m1.xlarge: Standard Extra Large Instance
4376              
4377             8 EC2 Compute Units (4 virtual cores with 2 EC2 Compute Units each). 64-bit, 15GB RAM, 1690GB disk
4378              
4379             =item t1.micro Micro Instance
4380              
4381             Up to 2 EC2 Compute Units (for short periodic bursts), 32-bit or 64-bit, 613MB RAM, EBS storage only
4382              
4383             =item c1.medium: High-CPU Medium Instance
4384              
4385             5 EC2 Compute Units (2 virutal cores with 2.5 EC2 Compute Units each). 32-bit or 64-bit, 1.7GB RAM, 350GB disk
4386              
4387             =item c1.xlarge: High-CPU Extra Large Instance
4388              
4389             20 EC2 Compute Units (8 virtual cores with 2.5 EC2 Compute Units each). 64-bit, 7GB RAM, 1690GB disk
4390              
4391             =item m2.2xlarge High-Memory Double Extra Large Instance
4392              
4393             13 EC2 Compute Units (4 virtual cores with 3.25 EC2 Compute Units each). 64-bit, 34.2GB RAM, 850GB disk
4394              
4395             =item m2.4xlarge High-Memory Quadruple Extra Large Instance
4396              
4397             26 EC2 Compute Units (8 virtual cores with 3.25 EC2 Compute Units each). 64-bit, 68.4GB RAM, 1690GB disk
4398              
4399             =item cc1.4xlarge Cluster Compute Quadruple Extra Large Instance
4400              
4401             33.5 EC2 Compute Units (2 x Intel Xeon X5570, quad-core "Nehalem" architecture), 64-bit, 23GB RAM, 1690GB disk, 10Gbit Ethernet
4402              
4403             =item cc1.8xlarge Cluster Compute Eight Extra Large Instance
4404              
4405             88 EC2 Compute Units (2 x Intel Xeon E5-2670, eight-core "Sandy Bridge" architecture), 64-bit, 60.5GB RAM, 3370GB disk, 10Gbit Ethernet
4406              
4407             =item cg1.4xlarge Cluster GPU Quadruple Extra Large Instance
4408              
4409             33.5 EC2 Compute Units (2 x Intel Xeon X5570, quad-core "Nehalem" architecture), 64-bit, 22GB RAM 1690GB disk, 10Gbit Ethernet, 2 x NVIDIA Tesla "Fermi" M2050 GPUs
4410              
4411             =item hi1.4xlarge High I/O Quadruple Extra Large Instance
4412              
4413             35 EC2 Compute Units (16 virtual cores), 60.5GB RAM, 64-bit, 2 x 1024GB SSD disk, 10Gbit Ethernet
4414              
4415             =back
4416              
4417             =item Placement.AvailabilityZone (optional)
4418              
4419             The availability zone you want to run the instance in
4420              
4421             =item KernelId (optional)
4422              
4423             The id of the kernel you want to launch the instance with
4424              
4425             =item RamdiskId (optional)
4426              
4427             The id of the ramdisk you want to launch the instance with
4428              
4429             =item BlockDeviceMapping.VirtualName (optional)
4430              
4431             This is the virtual name for a blocked device to be attached, may pass in a scalar or arrayref
4432              
4433             =item BlockDeviceMapping.DeviceName (optional)
4434              
4435             This is the device name for a block device to be attached, may pass in a scalar or arrayref
4436              
4437             =item Encoding (optional)
4438              
4439             The encoding.
4440              
4441             =item Version (optional)
4442              
4443             The version.
4444              
4445             =item Monitoring.Enabled (optional)
4446              
4447             Enables monitoring for this instance.
4448              
4449             =item SubnetId (optional)
4450              
4451             Specifies the subnet ID within which to launch the instance(s) for Amazon Virtual Private Cloud.
4452              
4453             =item ClientToken (optional)
4454              
4455             Specifies the idempotent instance id.
4456              
4457             =item EbsOptimized (optional)
4458              
4459             Whether the instance is optimized for EBS I/O.
4460              
4461             =item PrivateIpAddress (optional)
4462              
4463             Specifies the private IP address to use when launching an Amazon VPC instance.
4464              
4465             =item IamInstanceProfile.Name (optional)
4466              
4467             Specifies the IAM profile to associate with the launched instance(s). This is the name of the role.
4468              
4469             =item IamInstanceProfile.Arn (optional)
4470              
4471             Specifies the IAM profile to associate with the launched instance(s). This is the ARN of the profile.
4472              
4473              
4474             =back
4475              
4476             Returns a Net::Amazon::EC2::ReservationInfo object
4477              
4478             =cut
4479              
4480             sub run_instances {
4481 0     0 1   my $self = shift;
4482 0           my %args = validate( @_, {
4483             ImageId => { type => SCALAR },
4484             MinCount => { type => SCALAR },
4485             MaxCount => { type => SCALAR },
4486             KeyName => { type => SCALAR, optional => 1 },
4487             SecurityGroup => { type => SCALAR | ARRAYREF, optional => 1 },
4488             SecurityGroupId => { type => SCALAR | ARRAYREF, optional => 1 },
4489             AddressingType => { type => SCALAR, optional => 1 },
4490             AdditionalInfo => { type => SCALAR, optional => 1 },
4491             UserData => { type => SCALAR, optional => 1 },
4492             InstanceType => { type => SCALAR, optional => 1 },
4493             'Placement.AvailabilityZone' => { type => SCALAR, optional => 1 },
4494             KernelId => { type => SCALAR, optional => 1 },
4495             RamdiskId => { type => SCALAR, optional => 1 },
4496             'BlockDeviceMapping.VirtualName' => { type => SCALAR | ARRAYREF, optional => 1 },
4497             'BlockDeviceMapping.DeviceName' => { type => SCALAR | ARRAYREF, optional => 1 },
4498             'BlockDeviceMapping.Ebs.SnapshotId' => { type => SCALAR | ARRAYREF, optional => 1 },
4499             'BlockDeviceMapping.Ebs.VolumeSize' => { type => SCALAR | ARRAYREF, optional => 1 },
4500             'BlockDeviceMapping.Ebs.VolumeType' => { type => SCALAR | ARRAYREF, optional => 1 },
4501             'BlockDeviceMapping.Ebs.DeleteOnTermination' => { type => SCALAR | ARRAYREF, optional => 1 },
4502             Encoding => { type => SCALAR, optional => 1 },
4503             Version => { type => SCALAR, optional => 1 },
4504             'Monitoring.Enabled' => { type => SCALAR, optional => 1 },
4505             SubnetId => { type => SCALAR, optional => 1 },
4506             DisableApiTermination => { type => SCALAR, optional => 1 },
4507             InstanceInitiatedShutdownBehavior => { type => SCALAR, optional => 1 },
4508             ClientToken => { type => SCALAR, optional => 1 },
4509             EbsOptimized => { type => SCALAR, optional => 1 },
4510             PrivateIpAddress => { type => SCALAR, optional => 1 },
4511             'IamInstanceProfile.Name' => { type => SCALAR, optional => 1 },
4512             'IamInstanceProfile.Arn' => { type => SCALAR, optional => 1 },
4513              
4514             });
4515            
4516             # If we have a array ref of instances lets split them out into their SecurityGroup.n format
4517 0 0         if (ref ($args{SecurityGroup}) eq 'ARRAY') {
4518 0           my $security_groups = delete $args{SecurityGroup};
4519 0           my $count = 1;
4520 0           foreach my $security_group (@{$security_groups}) {
  0            
4521 0           $args{"SecurityGroup." . $count} = $security_group;
4522 0           $count++;
4523             }
4524             }
4525              
4526             # If we have a array ref of instances lets split them out into their SecurityGroupId.n format
4527 0 0         if (ref ($args{SecurityGroupId}) eq 'ARRAY') {
4528 0           my $security_groups = delete $args{SecurityGroupId};
4529 0           my $count = 1;
4530 0           foreach my $security_group (@{$security_groups}) {
  0            
4531 0           $args{"SecurityGroupId." . $count} = $security_group;
4532 0           $count++;
4533             }
4534             }
4535              
4536             # If we have a array ref of block device virtual names lets split them out into their BlockDeviceMapping.n.VirtualName format
4537 0 0         if (ref ($args{'BlockDeviceMapping.VirtualName'}) eq 'ARRAY') {
4538 0           my $virtual_names = delete $args{'BlockDeviceMapping.VirtualName'};
4539 0           my $count = 1;
4540 0           foreach my $virtual_name (@{$virtual_names}) {
  0            
4541 0 0         $args{"BlockDeviceMapping." . $count . ".VirtualName"} = $virtual_name if defined($virtual_name);
4542 0           $count++;
4543             }
4544             }
4545              
4546             # If we have a array ref of block device virtual names lets split them out into their BlockDeviceMapping.n.DeviceName format
4547 0 0         if (ref ($args{'BlockDeviceMapping.DeviceName'}) eq 'ARRAY') {
4548 0           my $device_names = delete $args{'BlockDeviceMapping.DeviceName'};
4549 0           my $count = 1;
4550 0           foreach my $device_name (@{$device_names}) {
  0            
4551 0 0         $args{"BlockDeviceMapping." . $count . ".DeviceName"} = $device_name if defined($device_name);
4552 0           $count++;
4553             }
4554             }
4555              
4556             # If we have a array ref of block device EBS Snapshots lets split them out into their BlockDeviceMapping.n.Ebs.SnapshotId format
4557 0 0         if (ref ($args{'BlockDeviceMapping.Ebs.SnapshotId'}) eq 'ARRAY') {
4558 0           my $snapshot_ids = delete $args{'BlockDeviceMapping.Ebs.SnapshotId'};
4559 0           my $count = 1;
4560 0           foreach my $snapshot_id (@{$snapshot_ids}) {
  0            
4561 0 0         $args{"BlockDeviceMapping." . $count . ".Ebs.SnapshotId"} = $snapshot_id if defined($snapshot_id);
4562 0           $count++;
4563             }
4564             }
4565              
4566             # If we have a array ref of block device EBS VolumeSizes lets split them out into their BlockDeviceMapping.n.Ebs.VolumeSize format
4567 0 0         if (ref ($args{'BlockDeviceMapping.Ebs.VolumeSize'}) eq 'ARRAY') {
4568 0           my $volume_sizes = delete $args{'BlockDeviceMapping.Ebs.VolumeSize'};
4569 0           my $count = 1;
4570 0           foreach my $volume_size (@{$volume_sizes}) {
  0            
4571 0 0         $args{"BlockDeviceMapping." . $count . ".Ebs.VolumeSize"} = $volume_size if defined($volume_size);
4572 0           $count++;
4573             }
4574             }
4575              
4576             # If we have a array ref of block device EBS VolumeTypes lets split them out into their BlockDeviceMapping.n.Ebs.VolumeType format
4577 0 0         if (ref ($args{'BlockDeviceMapping.Ebs.VolumeType'}) eq 'ARRAY') {
4578 0           my $volume_types = delete $args{'BlockDeviceMapping.Ebs.VolumeType'};
4579 0           my $count = 1;
4580 0           foreach my $volume_type (@{$volume_types}) {
  0            
4581 0 0         $args{"BlockDeviceMapping." . $count . ".Ebs.VolumeType"} = $volume_type if defined($volume_type);
4582 0           $count++;
4583             }
4584             }
4585              
4586             # If we have a array ref of block device EBS DeleteOnTerminations lets split them out into their BlockDeviceMapping.n.Ebs.DeleteOnTermination format
4587 0 0         if (ref ($args{'BlockDeviceMapping.Ebs.DeleteOnTermination'}) eq 'ARRAY') {
4588 0           my $terminations = delete $args{'BlockDeviceMapping.Ebs.DeleteOnTermination'};
4589 0           my $count = 1;
4590 0           foreach my $termination (@{$terminations}) {
  0            
4591 0           $args{"BlockDeviceMapping." . $count . ".Ebs.DeleteOnTermination"} = $termination;
4592 0           $count++;
4593             }
4594             }
4595              
4596 0           my $xml = $self->_sign(Action => 'RunInstances', %args);
4597            
4598 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4599 0           return $self->_parse_errors($xml);
4600             }
4601             else {
4602 0           my $group_sets=[];
4603 0           foreach my $group_arr (@{$xml->{groupSet}{item}}) {
  0            
4604             my $group = Net::Amazon::EC2::GroupSet->new(
4605             group_id => $group_arr->{groupId},
4606             group_name => $group_arr->{groupName},
4607 0           );
4608 0           push @$group_sets, $group;
4609             }
4610              
4611 0           my $running_instances;
4612 0           foreach my $instance_elem (@{$xml->{instancesSet}{item}}) {
  0            
4613             my $instance_state_type = Net::Amazon::EC2::InstanceState->new(
4614             code => $instance_elem->{instanceState}{code},
4615             name => $instance_elem->{instanceState}{name},
4616 0           );
4617            
4618 0           my $product_codes;
4619             my $state_reason;
4620 0           my $block_device_mappings;
4621            
4622 0 0         if (grep { defined && length } $instance_elem->{productCodes} ) {
  0 0          
4623 0           foreach my $pc (@{$instance_elem->{productCodes}{item}}) {
  0            
4624 0           my $product_code = Net::Amazon::EC2::ProductCode->new( product_code => $pc->{productCode} );
4625 0           push @$product_codes, $product_code;
4626             }
4627             }
4628              
4629 0 0 0       unless ( grep { defined && length } $instance_elem->{reason} and ref $instance_elem->{reason} ne 'HASH' ) {
  0 0          
4630 0           $instance_elem->{reason} = undef;
4631             }
4632              
4633 0 0 0       unless ( grep { defined && length } $instance_elem->{privateDnsName} and ref $instance_elem->{privateDnsName} ne 'HASH') {
  0 0          
4634 0           $instance_elem->{privateDnsName} = undef;
4635             }
4636              
4637 0 0 0       unless ( grep { defined && length } $instance_elem->{dnsName} and ref $instance_elem->{dnsName} ne 'HASH') {
  0 0          
4638 0           $instance_elem->{dnsName} = undef;
4639             }
4640              
4641 0 0         if ( grep { defined && length } $instance_elem->{stateReason} ) {
  0 0          
4642             $state_reason = Net::Amazon::EC2::StateReason->new(
4643             code => $instance_elem->{stateReason}{code},
4644             message => $instance_elem->{stateReason}{message},
4645 0           );
4646             }
4647              
4648 0 0         if ( grep { defined && length } $instance_elem->{blockDeviceMapping} ) {
  0 0          
4649 0           foreach my $bdm ( @{$instance_elem->{blockDeviceMapping}{item}} ) {
  0            
4650             my $ebs_block_device_mapping = Net::Amazon::EC2::EbsInstanceBlockDeviceMapping->new(
4651             volume_id => $bdm->{ebs}{volumeId},
4652             status => $bdm->{ebs}{status},
4653             attach_time => $bdm->{ebs}{attachTime},
4654             delete_on_termination => $bdm->{ebs}{deleteOnTermination},
4655 0           );
4656            
4657             my $block_device_mapping = Net::Amazon::EC2::BlockDeviceMapping->new(
4658             ebs => $ebs_block_device_mapping,
4659             device_name => $bdm->{deviceName},
4660 0           );
4661 0           push @$block_device_mappings, $block_device_mapping;
4662             }
4663             }
4664              
4665 0           my $placement_response = Net::Amazon::EC2::PlacementResponse->new( availability_zone => $instance_elem->{placement}{availabilityZone} );
4666            
4667             my $running_instance = Net::Amazon::EC2::RunningInstances->new(
4668             ami_launch_index => $instance_elem->{amiLaunchIndex},
4669             dns_name => $instance_elem->{dnsName},
4670             image_id => $instance_elem->{imageId},
4671             kernel_id => $instance_elem->{kernelId},
4672             ramdisk_id => $instance_elem->{ramdiskId},
4673             instance_id => $instance_elem->{instanceId},
4674             instance_state => $instance_state_type,
4675             instance_type => $instance_elem->{instanceType},
4676             key_name => $instance_elem->{keyName},
4677             launch_time => $instance_elem->{launchTime},
4678             placement => $placement_response,
4679             private_dns_name => $instance_elem->{privateDnsName},
4680             reason => $instance_elem->{reason},
4681             platform => $instance_elem->{platform},
4682             monitoring => $instance_elem->{monitoring}{state},
4683             subnet_id => $instance_elem->{subnetId},
4684             vpc_id => $instance_elem->{vpcId},
4685             private_ip_address => $instance_elem->{privateIpAddress},
4686             ip_address => $instance_elem->{ipAddress},
4687             architecture => $instance_elem->{architecture},
4688             root_device_name => $instance_elem->{rootDeviceName},
4689             root_device_type => $instance_elem->{rootDeviceType},
4690 0           block_device_mapping => $block_device_mappings,
4691             state_reason => $state_reason,
4692             );
4693              
4694 0 0         if ($product_codes) {
4695 0           $running_instance->product_codes($product_codes);
4696             }
4697            
4698 0           push @$running_instances, $running_instance;
4699             }
4700            
4701             my $reservation = Net::Amazon::EC2::ReservationInfo->new(
4702             reservation_id => $xml->{reservationId},
4703             owner_id => $xml->{ownerId},
4704 0           group_set => $group_sets,
4705             instances_set => $running_instances,
4706             );
4707            
4708 0           return $reservation;
4709             }
4710             }
4711              
4712             =head2 start_instances(%params)
4713              
4714             Starts an instance that uses an Amazon EBS volume as its root device.
4715              
4716             =over
4717              
4718             =item InstanceId (required)
4719              
4720             Either a scalar or an array ref can be passed in (containing instance ids to be started).
4721              
4722             =back
4723              
4724             Returns an array ref of Net::Amazon::EC2::InstanceStateChange objects.
4725              
4726             =cut
4727              
4728             sub start_instances {
4729 0     0 1   my $self = shift;
4730 0           my %args = validate( @_, {
4731             InstanceId => { type => SCALAR | ARRAYREF },
4732             });
4733            
4734             # If we have a array ref of instances lets split them out into their InstanceId.n format
4735 0 0         if (ref ($args{InstanceId}) eq 'ARRAY') {
4736 0           my $instance_ids = delete $args{InstanceId};
4737 0           my $count = 1;
4738 0           foreach my $instance_id (@{$instance_ids}) {
  0            
4739 0           $args{"InstanceId." . $count} = $instance_id;
4740 0           $count++;
4741             }
4742             }
4743            
4744 0           my $xml = $self->_sign(Action => 'StartInstances', %args);
4745 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4746 0           return $self->_parse_errors($xml);
4747             }
4748             else {
4749 0           my $started_instances;
4750            
4751 0           foreach my $inst (@{$xml->{instancesSet}{item}}) {
  0            
4752             my $previous_state = Net::Amazon::EC2::InstanceState->new(
4753             code => $inst->{previousState}{code},
4754             name => $inst->{previousState}{name},
4755 0           );
4756            
4757             my $current_state = Net::Amazon::EC2::InstanceState->new(
4758             code => $inst->{currentState}{code},
4759             name => $inst->{currentState}{name},
4760 0           );
4761              
4762             my $started_instance = Net::Amazon::EC2::InstanceStateChange->new(
4763             instance_id => $inst->{instanceId},
4764 0           previous_state => $previous_state,
4765             current_state => $current_state,
4766             );
4767            
4768 0           push @$started_instances, $started_instance;
4769             }
4770            
4771 0           return $started_instances;
4772             }
4773             }
4774              
4775             =head2 stop_instances(%params)
4776              
4777             Stops an instance that uses an Amazon EBS volume as its root device.
4778              
4779             =over
4780              
4781             =item InstanceId (required)
4782              
4783             Either a scalar or an array ref can be passed in (containing instance ids to be stopped).
4784              
4785             =item Force (optional)
4786              
4787             If set to true, forces the instance to stop. The instance will not have an opportunity to
4788             flush file system caches nor file system meta data. If you use this option, you must perform file
4789             system check and repair procedures. This option is not recommended for Windows instances.
4790              
4791             The default is false.
4792              
4793             =back
4794              
4795             Returns an array ref of Net::Amazon::EC2::InstanceStateChange objects.
4796              
4797             =cut
4798              
4799             sub stop_instances {
4800 0     0 1   my $self = shift;
4801 0           my %args = validate( @_, {
4802             InstanceId => { type => SCALAR | ARRAYREF },
4803             Force => { type => SCALAR, optional => 1 },
4804             });
4805            
4806             # If we have a array ref of instances lets split them out into their InstanceId.n format
4807 0 0         if (ref ($args{InstanceId}) eq 'ARRAY') {
4808 0           my $instance_ids = delete $args{InstanceId};
4809 0           my $count = 1;
4810 0           foreach my $instance_id (@{$instance_ids}) {
  0            
4811 0           $args{"InstanceId." . $count} = $instance_id;
4812 0           $count++;
4813             }
4814             }
4815            
4816 0           my $xml = $self->_sign(Action => 'StopInstances', %args);
4817 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4818 0           return $self->_parse_errors($xml);
4819             }
4820             else {
4821 0           my $stopped_instances;
4822            
4823 0           foreach my $inst (@{$xml->{instancesSet}{item}}) {
  0            
4824             my $previous_state = Net::Amazon::EC2::InstanceState->new(
4825             code => $inst->{previousState}{code},
4826             name => $inst->{previousState}{name},
4827 0           );
4828            
4829             my $current_state = Net::Amazon::EC2::InstanceState->new(
4830             code => $inst->{currentState}{code},
4831             name => $inst->{currentState}{name},
4832 0           );
4833              
4834             my $stopped_instance = Net::Amazon::EC2::InstanceStateChange->new(
4835             instance_id => $inst->{instanceId},
4836 0           previous_state => $previous_state,
4837             current_state => $current_state,
4838             );
4839            
4840 0           push @$stopped_instances, $stopped_instance;
4841             }
4842            
4843 0           return $stopped_instances;
4844             }
4845             }
4846              
4847             =head2 terminate_instances(%params)
4848              
4849             This method shuts down instance(s) passed into it. It takes the following parameter:
4850              
4851             =over
4852              
4853             =item InstanceId (required)
4854              
4855             Either a scalar or an array ref can be passed in (containing instance ids)
4856              
4857             =back
4858              
4859             Returns an array ref of Net::Amazon::EC2::InstanceStateChange objects.
4860              
4861             =cut
4862              
4863             sub terminate_instances {
4864 0     0 1   my $self = shift;
4865 0           my %args = validate( @_, {
4866             InstanceId => { type => SCALAR | ARRAYREF },
4867             });
4868            
4869             # If we have a array ref of instances lets split them out into their InstanceId.n format
4870 0 0         if (ref ($args{InstanceId}) eq 'ARRAY') {
4871 0           my $instance_ids = delete $args{InstanceId};
4872 0           my $count = 1;
4873 0           foreach my $instance_id (@{$instance_ids}) {
  0            
4874 0           $args{"InstanceId." . $count} = $instance_id;
4875 0           $count++;
4876             }
4877             }
4878            
4879 0           my $xml = $self->_sign(Action => 'TerminateInstances', %args);
4880 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4881 0           return $self->_parse_errors($xml);
4882             }
4883             else {
4884 0           my $terminated_instances;
4885            
4886 0           foreach my $inst (@{$xml->{instancesSet}{item}}) {
  0            
4887             my $previous_state = Net::Amazon::EC2::InstanceState->new(
4888             code => $inst->{previousState}{code},
4889             name => $inst->{previousState}{name},
4890 0           );
4891            
4892             my $current_state = Net::Amazon::EC2::InstanceState->new(
4893             code => $inst->{currentState}{code},
4894             name => $inst->{currentState}{name},
4895 0           );
4896              
4897             # Note, this is a bit of a backwards incompatible change in so much as I am changing
4898             # return class for this. I hate to do it but I need to be consistent with this
4899             # now being a instance stage change object. This used to be a
4900             # Net::Amazon::EC2::TerminateInstancesResponse object.
4901             my $terminated_instance = Net::Amazon::EC2::InstanceStateChange->new(
4902             instance_id => $inst->{instanceId},
4903 0           previous_state => $previous_state,
4904             current_state => $current_state,
4905             );
4906            
4907 0           push @$terminated_instances, $terminated_instance;
4908             }
4909            
4910 0           return $terminated_instances;
4911             }
4912             }
4913              
4914             =head2 unmonitor_instances(%params)
4915              
4916             Disables monitoring for a running instance. For more information, refer to the Amazon CloudWatch Developer Guide.
4917              
4918             =over
4919              
4920             =item InstanceId (required)
4921              
4922             The instance id(s) to monitor. Can be a scalar or an array ref
4923              
4924             =back
4925              
4926             Returns an array ref of Net::Amazon::EC2::MonitoredInstance objects
4927              
4928             =cut
4929              
4930             sub unmonitor_instances {
4931 0     0 1   my $self = shift;
4932 0           my %args = validate( @_, {
4933             InstanceId => { type => ARRAYREF | SCALAR, optional => 1 },
4934             });
4935              
4936             # If we have a array ref of instances lets split them out into their InstanceId.n format
4937 0 0         if (ref ($args{InstanceId}) eq 'ARRAY') {
4938 0           my $instance_ids = delete $args{InstanceId};
4939 0           my $count = 1;
4940 0           foreach my $instance_id (@{$instance_ids}) {
  0            
4941 0           $args{"InstanceId." . $count} = $instance_id;
4942 0           $count++;
4943             }
4944             }
4945            
4946 0           my $xml = $self->_sign(Action => 'UnmonitorInstances', %args);
4947            
4948 0 0         if ( grep { defined && length } $xml->{Errors} ) {
  0 0          
4949 0           return $self->_parse_errors($xml);
4950             }
4951             else {
4952 0           my $monitored_instances;
4953              
4954 0           foreach my $monitored_instance_item (@{$xml->{instancesSet}{item}}) {
  0            
4955             my $monitored_instance = Net::Amazon::EC2::ReservedInstance->new(
4956             instance_id => $monitored_instance_item->{instanceId},
4957             state => $monitored_instance_item->{monitoring}{state},
4958 0           );
4959            
4960 0           push @$monitored_instances, $monitored_instance;
4961             }
4962            
4963 0           return $monitored_instances;
4964             }
4965             }
4966              
4967 1     1   13 no Moose;
  1         2  
  1         10  
4968             1;
4969              
4970             __END__
4971              
4972             =head1 TESTING
4973              
4974             Set AWS_ACCESS_KEY_ID and SECRET_ACCESS_KEY environment variables to run the live tests.
4975             Note: because the live tests start an instance (and kill it) in both the tests and backwards compat tests there will be 2 hours of
4976             machine instance usage charges (since there are 2 instances started) which as of February 1st, 2010 costs a total of $0.17 USD
4977              
4978             Important note about the windows-only methods. These have not been well tested as I do not run windows-based instances, so exercise
4979             caution in using these.
4980              
4981             =head1 BUGS
4982              
4983             Please report any bugs or feature requests to C<bug-net-amazon-ec2 at rt.cpan.org>, or through
4984             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-Amazon-EC2>. I will
4985             be notified, and then you'll automatically be notified of progress on your bug as I make changes.
4986              
4987             =head1 AUTHOR
4988              
4989             Jeff Kim <cpan@chosec.com>
4990              
4991             =head1 CONTRIBUTORS
4992              
4993             John McCullough and others as listed in the Changelog
4994              
4995             =head1 MAINTAINER
4996              
4997             The current maintainer is Mark Allen C<< <mallen@cpan.org> >>
4998              
4999             =head1 COPYRIGHT
5000              
5001             Copyright (c) 2006-2010 Jeff Kim.
5002              
5003             Copyright (c) 2012 Mark Allen.
5004              
5005             This program is free software; you can redistribute it and/or modify it
5006             under the same terms as Perl itself.
5007              
5008             =head1 SEE ALSO
5009              
5010             Amazon EC2 API: L<http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/>
5011              
5012             =cut