File Coverage

blib/lib/Net/Amazon/EC2.pm
Criterion Covered Total %
statement 297 1463 20.3
branch 19 630 3.0
condition 4 54 7.4
subroutine 76 141 53.9
pod 58 61 95.0
total 454 2349 19.3


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