| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package VM::EC2::Dispatch; |
|
2
|
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
26
|
use strict; |
|
|
7
|
|
|
|
|
8
|
|
|
|
7
|
|
|
|
|
211
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
1526
|
use XML::Simple; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
use URI::Escape; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
VM::EC2::Dispatch - Create Perl objects from AWS XML requests |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use VM::EC2; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
VM::EC2::Dispatch->register('DescribeRegions'=>\&mysub); |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
VM::EC2::Dispatch->replace('DescribeRegions'=>'My::Type'); |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub mysub { |
|
21
|
|
|
|
|
|
|
my ($parsed_xml_object,$ec2) = @_; |
|
22
|
|
|
|
|
|
|
my $payload = $parsed_xml_object->{regionInfo} |
|
23
|
|
|
|
|
|
|
return My::Type->new($payload,$ec2); |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
This class handles turning the XML response to AWS requests into perl |
|
29
|
|
|
|
|
|
|
objects. Only one method is likely to be useful to developers, the |
|
30
|
|
|
|
|
|
|
replace() class method. This allows you to replace the handlers |
|
31
|
|
|
|
|
|
|
used to map the response onto objects. |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 VM::EC2::Dispatch->replace($request_name => \&sub) |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head2 VM::EC2::Dispatch->replace($request_name => 'Class::Name') |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head2 VM::EC2::Dispatch->replace($request_name => 'method_name,arg1,arg2,...') |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Before invoking a VM::EC2 request you wish to customize, call the |
|
40
|
|
|
|
|
|
|
replace() method with two arguments. The first argument is the |
|
41
|
|
|
|
|
|
|
name of the request you wish to customize, such as |
|
42
|
|
|
|
|
|
|
"DescribeVolumes". The second argument is either a code reference, a |
|
43
|
|
|
|
|
|
|
VM::EC2::Dispatch method name and arguments (separated by commas), or |
|
44
|
|
|
|
|
|
|
a class name. |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
In the case of a code reference as the second argument, the subroutine |
|
47
|
|
|
|
|
|
|
you provide will be invoked with four arguments consisting of the |
|
48
|
|
|
|
|
|
|
parsed XML response, the VM::EC2 object, the XML namespace string from |
|
49
|
|
|
|
|
|
|
the request, and the Amazon-assigned request ID. In practice, only the |
|
50
|
|
|
|
|
|
|
first two arguments are useful. |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
In the case of a string containing a classname, the class will be |
|
53
|
|
|
|
|
|
|
loaded if it needs to be, and then its new() method invoked as |
|
54
|
|
|
|
|
|
|
follows: |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Your::Class->new($parsed_xml,$ec2,$xmlns,$requestid) |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Your new() method should return one or more objects. It is suggested |
|
59
|
|
|
|
|
|
|
that you subclass VM::EC2::Generic and use the inherited new() method |
|
60
|
|
|
|
|
|
|
to store the parsed XML and EC2 object. See the code for |
|
61
|
|
|
|
|
|
|
L for a simple template. |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
If the second argument is neither a code reference nor a classname, it |
|
64
|
|
|
|
|
|
|
will be treated as a VM::EC2::Dispatch method name and its arguments, |
|
65
|
|
|
|
|
|
|
separated by commas. The method will be invoked as follows: |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
$dispatch->$method_name($raw_xml,$ec2,$arg1,$arg2,$arg3,...) |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
There are two methods currently defined for this purpose, boolean(), |
|
70
|
|
|
|
|
|
|
and fetch_items(), which handle the preprocessing of several common |
|
71
|
|
|
|
|
|
|
XML representations of EC2 data. Note that in this form, the RAW XML |
|
72
|
|
|
|
|
|
|
is passed in, not the parsed data structure. |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
The parsed XML response is generated by the XML::Simple module using |
|
75
|
|
|
|
|
|
|
these options: |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
$parser = XML::Simple->new(ForceArray => ['item', 'member'], |
|
78
|
|
|
|
|
|
|
KeyAttr => ['key'], |
|
79
|
|
|
|
|
|
|
SuppressEmpty => undef); |
|
80
|
|
|
|
|
|
|
$parsed = $parser->XMLin($raw_xml) |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
In general, this will give you a hash of hashes. Any tag named 'item' |
|
83
|
|
|
|
|
|
|
or 'member' will be forced to point to an array reference, and any tag |
|
84
|
|
|
|
|
|
|
named "key" will be flattened as described in the XML::Simple |
|
85
|
|
|
|
|
|
|
documentation. |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
A simple way to examine the raw parsed XML is to invoke any |
|
88
|
|
|
|
|
|
|
VM::EC2::Object's as_string method: |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
my ($i) = $ec2->describe_instances; |
|
91
|
|
|
|
|
|
|
print $i->as_string; |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
This will give you a Data::Dumper representation of the XML after it |
|
94
|
|
|
|
|
|
|
has been parsed. Look at the calls to VM::EC2::Dispatch->register() in |
|
95
|
|
|
|
|
|
|
the various VM/EC2/REST/*.pm modules for many examples of how this |
|
96
|
|
|
|
|
|
|
works. |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Note that the replace() method was called add_override() in previous |
|
99
|
|
|
|
|
|
|
versions of this module. add_override() is recognized as an alias for |
|
100
|
|
|
|
|
|
|
backward compatibility. |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=head2 VM::EC2::Dispatch->register($request_name1 => \&sub1,$request_name2 => \&sub2,...) |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Similar to replace() but if the request name is already registered |
|
105
|
|
|
|
|
|
|
does not overwrite it. You may provide multiple request=>handler pairs. |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head1 OBJECT CREATION METHODS |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
The following methods perform simple pre-processing of the parsed XML |
|
110
|
|
|
|
|
|
|
(a hash of hashes) before passing the modified data structure to the |
|
111
|
|
|
|
|
|
|
designated object class. They are used as the second argument to |
|
112
|
|
|
|
|
|
|
VM::EC2::Dispatch->register(). |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=cut |
|
115
|
|
|
|
|
|
|
; |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
my $REGISTRATION = {}; |
|
118
|
|
|
|
|
|
|
VM::EC2::Dispatch->register(Error => 'VM::EC2::Error'); |
|
119
|
|
|
|
|
|
|
*add_override = \&replace; # backward compatibility |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# Not clear that you ever need to instantiate this object as it has |
|
122
|
|
|
|
|
|
|
# no instance data. |
|
123
|
|
|
|
|
|
|
sub new { |
|
124
|
|
|
|
|
|
|
my $class = shift; |
|
125
|
|
|
|
|
|
|
my $self= bless {},ref $class || $class; |
|
126
|
|
|
|
|
|
|
return $self; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub replace { |
|
130
|
|
|
|
|
|
|
my $self = shift; |
|
131
|
|
|
|
|
|
|
while (my ($request_name,$object_creator) = splice(@_,0,2)) { |
|
132
|
|
|
|
|
|
|
$REGISTRATION->{$request_name} = $object_creator; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub register { |
|
137
|
|
|
|
|
|
|
my $self = shift; |
|
138
|
|
|
|
|
|
|
while (my ($request_name,$object_creator) = splice(@_,0,2)) { |
|
139
|
|
|
|
|
|
|
$REGISTRATION->{$request_name} ||= $object_creator; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# new way |
|
144
|
|
|
|
|
|
|
sub content2objects { |
|
145
|
|
|
|
|
|
|
my $self = shift; |
|
146
|
|
|
|
|
|
|
my ($action,$content,$ec2) = @_; |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $handler = $REGISTRATION->{$action} || 'VM::EC2::Generic'; |
|
149
|
|
|
|
|
|
|
my ($method,@params) = split /,/,$handler; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
if (ref $handler eq 'CODE') { |
|
152
|
|
|
|
|
|
|
my $parsed = $self->new_xml_parser->XMLin($content); |
|
153
|
|
|
|
|
|
|
my $req_id_tag = $parsed->{requestId} ? 'requestId' : 'RequestId'; |
|
154
|
|
|
|
|
|
|
$handler->($parsed,$ec2,@{$parsed}{'xmlns',$req_id_tag}); |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
elsif ($self->can($method)) { |
|
157
|
|
|
|
|
|
|
return $self->$method($content,$ec2,@params); |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
else { |
|
160
|
|
|
|
|
|
|
load_module($handler); |
|
161
|
|
|
|
|
|
|
my $parser = $self->new(); |
|
162
|
|
|
|
|
|
|
$parser->parse($content,$ec2,$handler); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub parser { |
|
167
|
|
|
|
|
|
|
my $self = shift; |
|
168
|
|
|
|
|
|
|
return $self->{xml_parser} ||= $self->new_xml_parser; |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub parse { |
|
172
|
|
|
|
|
|
|
my $self = shift; |
|
173
|
|
|
|
|
|
|
my ($content,$ec2,$class) = @_; |
|
174
|
|
|
|
|
|
|
$self = $self->new unless ref $self; |
|
175
|
|
|
|
|
|
|
my $parsed = $self->parser->XMLin($content); |
|
176
|
|
|
|
|
|
|
return $self->create_objects($parsed,$ec2,$class); |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub new_xml_parser { |
|
180
|
|
|
|
|
|
|
my $self = shift; |
|
181
|
|
|
|
|
|
|
my $nokey = shift; |
|
182
|
|
|
|
|
|
|
return XML::Simple->new(ForceArray => ['item', 'member'], |
|
183
|
|
|
|
|
|
|
KeyAttr => $nokey ? [] : ['key'], |
|
184
|
|
|
|
|
|
|
SuppressEmpty => undef, |
|
185
|
|
|
|
|
|
|
); |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=head2 $bool = $dispatch->boolean($raw_xml,$ec2,$tag) |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
This is used for XML responses like this: |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
59dbff89-35bd-4eac-99ed-be587EXAMPLE |
|
194
|
|
|
|
|
|
|
true |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
It looks inside the structure for the tag named $tag ("return" if not |
|
198
|
|
|
|
|
|
|
provided), and returns a true value if the contents equals "true". |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Pass it to replace() like this: |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
VM::EC2::Dispatch->replace(DeleteVolume => 'boolean,return'; |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
or, since "return" is the default tag: |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
VM::EC2::Dispatch->replace(DeleteVolume => 'boolean'; |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=cut |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub boolean { |
|
211
|
|
|
|
|
|
|
my $self = shift; |
|
212
|
|
|
|
|
|
|
my ($content,$ec2,$tag) = @_; |
|
213
|
|
|
|
|
|
|
my $parsed = $self->new_xml_parser()->XMLin($content); |
|
214
|
|
|
|
|
|
|
$tag ||= 'return'; |
|
215
|
|
|
|
|
|
|
return $parsed->{$tag} eq 'true'; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=head2 @list = $dispatch->elb_member_list($raw_xml,$ec2,$tag) |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
This is used for XML responses from the ELB API such as this: |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
us-west-2a |
|
226
|
|
|
|
|
|
|
us-west-2b |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
02eadcfc-fc38-11e1-a1bf-9de31EXAMPLE |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
It looks inside the Result structure for the tag named $tag and returns the |
|
235
|
|
|
|
|
|
|
list wrapped in member elements. In this case the tag is 'AvailabilityZones' |
|
236
|
|
|
|
|
|
|
and the return value would be: |
|
237
|
|
|
|
|
|
|
( 'us-west-2a', 'us-west-2b' ) |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
If $embedded_tag is passed, then it is used for XML responses such as this, |
|
240
|
|
|
|
|
|
|
where the member list has an embedded tag: |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
i-12345678 |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
i-90abcdef |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
f4f12596-fc3b-11e1-be5a-f71ecEXAMPLE |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
It looks inside the Result structure for the tag named $tag and returns the |
|
259
|
|
|
|
|
|
|
list wrapped in a member element plus the embedded tag. In this case the |
|
260
|
|
|
|
|
|
|
tag is 'Instances', the embedded tag is 'InstanceId' and the return value would |
|
261
|
|
|
|
|
|
|
be: ( 'i-12345678', 'i-90abcdef' ) |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=cut |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub elb_member_list { |
|
266
|
|
|
|
|
|
|
my $self = shift; |
|
267
|
|
|
|
|
|
|
my ($content,$ec2,$tag,$embedded_tag) = @_; |
|
268
|
|
|
|
|
|
|
my $parsed = $self->new_xml_parser()->XMLin($content); |
|
269
|
|
|
|
|
|
|
my ($result_key) = grep /Result$/,keys %$parsed; |
|
270
|
|
|
|
|
|
|
return $embedded_tag ? map { $_->{$embedded_tag} } @{$parsed->{$result_key}{$tag}{member}} : |
|
271
|
|
|
|
|
|
|
@{$parsed->{$result_key}{$tag}{member}}; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# identical to fetch_one, except looks inside the (APICallName)Result tag that |
|
275
|
|
|
|
|
|
|
# ELB and RDS API calls return |
|
276
|
|
|
|
|
|
|
sub fetch_one_result { |
|
277
|
|
|
|
|
|
|
my $self = shift; |
|
278
|
|
|
|
|
|
|
my ($content,$ec2,$tag,$class,$nokey) = @_; |
|
279
|
|
|
|
|
|
|
load_module($class); |
|
280
|
|
|
|
|
|
|
my $parser = $self->new_xml_parser($nokey); |
|
281
|
|
|
|
|
|
|
my $parsed = $parser->XMLin($content); |
|
282
|
|
|
|
|
|
|
my ($result_key) = grep /Result$/,keys %$parsed; |
|
283
|
|
|
|
|
|
|
my $obj = $parsed->{$result_key}{$tag} or return; |
|
284
|
|
|
|
|
|
|
return $class->new($obj,$ec2,@{$parsed}{'xmlns','RequestId'}); |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub fetch_one { |
|
288
|
|
|
|
|
|
|
my $self = shift; |
|
289
|
|
|
|
|
|
|
my ($content,$ec2,$tag,$class,$nokey) = @_; |
|
290
|
|
|
|
|
|
|
load_module($class); |
|
291
|
|
|
|
|
|
|
my $parser = $self->new_xml_parser($nokey); |
|
292
|
|
|
|
|
|
|
my $parsed = $parser->XMLin($content); |
|
293
|
|
|
|
|
|
|
my $obj = $parsed->{$tag} or return; |
|
294
|
|
|
|
|
|
|
return $class->new($obj,$ec2,@{$parsed}{'xmlns','requestId'}); |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head2 @objects = $dispatch->fetch_items($raw_xml,$ec2,$container_tag,$object_class,$nokey) |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
This is used for XML responses like this: |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
59dbff89-35bd-4eac-99ed-be587EXAMPLE |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
-
|
|
305
|
|
|
|
|
|
|
gsg-keypair |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
-
|
|
311
|
|
|
|
|
|
|
default-keypair |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
0a:93:bb:e8:c2:89:e9:d8:1f:42:5d:37:1d:8d:b8:0a:88:f1:f1:1a |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
It looks inside the structure for the tag named $container_tag, pulls |
|
320
|
|
|
|
|
|
|
out the items that are stored under - and then passes the parsed
|
|
321
|
|
|
|
|
|
|
contents to $object_class->new(). The optional $nokey argument is used |
|
322
|
|
|
|
|
|
|
to suppress XML::Simple's default flattening behavior turning tags |
|
323
|
|
|
|
|
|
|
named "key" into hash keys. |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Pass it to replace() like this: |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
VM::EC2::Dispatch->replace(DescribeVolumes => 'fetch_items,volumeSet,VM::EC2::Volume') |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=cut |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub fetch_items { |
|
332
|
|
|
|
|
|
|
my $self = shift; |
|
333
|
|
|
|
|
|
|
my ($content,$ec2,$tag,$class,$nokey) = @_; |
|
334
|
|
|
|
|
|
|
load_module($class); |
|
335
|
|
|
|
|
|
|
my $parser = $self->new_xml_parser($nokey); |
|
336
|
|
|
|
|
|
|
my $parsed = $parser->XMLin($content); |
|
337
|
|
|
|
|
|
|
my $list = $parsed->{$tag}{item} or return; |
|
338
|
|
|
|
|
|
|
return map {$class->new($_,$ec2,@{$parsed}{'xmlns','requestId'})} @$list; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head2 @objects = $dispatch->fetch_members($raw_xml,$ec2,$container_tag,$object_class,$nokey) |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
Used for XML responses from ELB API calls which contain a key that is the name |
|
344
|
|
|
|
|
|
|
of the API call with 'Result' appended. All these XML responses contain |
|
345
|
|
|
|
|
|
|
'member' as the item delimter instead of 'item' |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=cut |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub fetch_members { |
|
350
|
|
|
|
|
|
|
my $self = shift; |
|
351
|
|
|
|
|
|
|
my ($content,$ec2,$tag,$class,$nokey) = @_; |
|
352
|
|
|
|
|
|
|
load_module($class); |
|
353
|
|
|
|
|
|
|
my $parser = $self->new_xml_parser($nokey); |
|
354
|
|
|
|
|
|
|
my $parsed = $parser->XMLin($content); |
|
355
|
|
|
|
|
|
|
my ($result_key) = grep /Result$/,keys %$parsed; |
|
356
|
|
|
|
|
|
|
my $list = $parsed->{$result_key}{$tag}{member} or return; |
|
357
|
|
|
|
|
|
|
return map {$class->new($_,$ec2,@{$parsed}{'xmlns','RequestId'})} @$list; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=head2 @objects = $dispatch->fetch_items_iterator($raw_xml,$ec2,$container_tag,$object_class,$token_name) |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
This is used for requests that have a -max_results argument. In this |
|
363
|
|
|
|
|
|
|
case, the response will have a nextToken field, which can be used to |
|
364
|
|
|
|
|
|
|
fetch the "next page" of results. |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
The $token_name is some unique identifying token. It will be turned |
|
367
|
|
|
|
|
|
|
into two temporary EC2 instance variables, one named |
|
368
|
|
|
|
|
|
|
"${token_name}_token", which contains the nextToken value, and the |
|
369
|
|
|
|
|
|
|
other "${token_name}_stop", which flags the caller that no more |
|
370
|
|
|
|
|
|
|
results will be forthcoming. |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
This must all be coordinated with the request subroutine. See how |
|
373
|
|
|
|
|
|
|
describe_instance_status() and describe_spot_price_history() do it. |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=cut |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub fetch_items_iterator { |
|
378
|
|
|
|
|
|
|
my $self = shift; |
|
379
|
|
|
|
|
|
|
my ($content,$ec2,$tag,$class,$base_name) = @_; |
|
380
|
|
|
|
|
|
|
my $token = "${base_name}_token"; |
|
381
|
|
|
|
|
|
|
my $stop = "${base_name}_stop"; |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
load_module($class); |
|
384
|
|
|
|
|
|
|
my $parser = $self->new_xml_parser(); |
|
385
|
|
|
|
|
|
|
my $parsed = $parser->XMLin($content); |
|
386
|
|
|
|
|
|
|
my $list = $parsed->{$tag}{item} or return; |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
if ($ec2->{$token} && !$parsed->{nextToken}) { |
|
389
|
|
|
|
|
|
|
delete $ec2->{$token}; |
|
390
|
|
|
|
|
|
|
$ec2->{$stop}++; |
|
391
|
|
|
|
|
|
|
} else { |
|
392
|
|
|
|
|
|
|
$ec2->{$token} = $parsed->{nextToken}; |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
return map {$class->new($_,$ec2,@{$parsed}{'xmlns','requestId'})} @$list; |
|
395
|
|
|
|
|
|
|
} |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub create_objects { |
|
398
|
|
|
|
|
|
|
my $self = shift; |
|
399
|
|
|
|
|
|
|
my ($parsed,$ec2,$class) = @_; |
|
400
|
|
|
|
|
|
|
return $class->new($parsed,$ec2,@{$parsed}{'xmlns','requestId'}); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub create_error_object { |
|
404
|
|
|
|
|
|
|
my $self = shift; |
|
405
|
|
|
|
|
|
|
my ($content,$ec2,$API_call) = @_; |
|
406
|
|
|
|
|
|
|
my $class = $REGISTRATION->{Error}; |
|
407
|
|
|
|
|
|
|
eval "require $class; 1" || die $@ unless $class->can('new'); |
|
408
|
|
|
|
|
|
|
my $parsed = $self->new_xml_parser->XMLin($content); |
|
409
|
|
|
|
|
|
|
if (defined $API_call) { |
|
410
|
|
|
|
|
|
|
$parsed->{Errors}{Error}{Message} =~ s/\.$//; |
|
411
|
|
|
|
|
|
|
$parsed->{Errors}{Error}{Message} .= ", at API call '$API_call'"; |
|
412
|
|
|
|
|
|
|
} |
|
413
|
|
|
|
|
|
|
return $class->new($parsed->{Errors}{Error},$ec2,@{$parsed}{'xmlns','RequestID'}); |
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# alternate method used for ELB, RDS calls |
|
417
|
|
|
|
|
|
|
sub create_alt_error_object { |
|
418
|
|
|
|
|
|
|
my $self = shift; |
|
419
|
|
|
|
|
|
|
my ($content,$ec2) = @_; |
|
420
|
|
|
|
|
|
|
my $class = 'VM::EC2::Error'; |
|
421
|
|
|
|
|
|
|
eval "require $class; 1" || die $@ unless $class->can('new'); |
|
422
|
|
|
|
|
|
|
my $parsed = $self->new_xml_parser->XMLin($content); |
|
423
|
|
|
|
|
|
|
return $class->new($parsed->{Error},$ec2,@{$parsed}{'xmlns','RequestId'}); |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# not a method! |
|
427
|
|
|
|
|
|
|
sub load_module { |
|
428
|
|
|
|
|
|
|
my $class = shift; |
|
429
|
|
|
|
|
|
|
eval "require $class; 1" || die $@ unless $class->can('new'); |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head1 EXAMPLE OF USING OVERRIDE TO SUBCLASS VM::EC2::Volume |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
The author decided that a volume object should not be able to delete |
|
435
|
|
|
|
|
|
|
itself; you disagree with that decision. Let's subclass |
|
436
|
|
|
|
|
|
|
VM::EC2::Volume to add a delete() method. |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
First subclass the VM::EC2::Volume class: |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
package MyVolume; |
|
441
|
|
|
|
|
|
|
use base 'VM::EC2::Volume'; |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
sub delete { |
|
444
|
|
|
|
|
|
|
my $self = shift; |
|
445
|
|
|
|
|
|
|
$self->ec2->delete_volume($self); |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
Now subclass VM::EC2 to add the appropriate overrides to the new() method: |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
package MyEC2; |
|
451
|
|
|
|
|
|
|
use base 'VM::EC2'; |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub new { |
|
454
|
|
|
|
|
|
|
my $class = shift; |
|
455
|
|
|
|
|
|
|
VM::EC2::Dispatch->replace(CreateVolume =>'MyVolume'); |
|
456
|
|
|
|
|
|
|
VM::EC2::Dispatch->replace(DescribeVolumes=>'fetch_items,volumeSet,MyVolume'); |
|
457
|
|
|
|
|
|
|
return $class->SUPER::new(@_); |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Now we can test it out: |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
use MyEC2; |
|
463
|
|
|
|
|
|
|
# find all volumes that are "available" and not in-use |
|
464
|
|
|
|
|
|
|
my @vol = $ec2->describe_volumes({status=>'available'}); |
|
465
|
|
|
|
|
|
|
for my $vol (@vol) { |
|
466
|
|
|
|
|
|
|
$vol->delete && print "$vol deleted\n" |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
L |
|
472
|
|
|
|
|
|
|
L |
|
473
|
|
|
|
|
|
|
L |
|
474
|
|
|
|
|
|
|
L |
|
475
|
|
|
|
|
|
|
L |
|
476
|
|
|
|
|
|
|
L |
|
477
|
|
|
|
|
|
|
L |
|
478
|
|
|
|
|
|
|
L |
|
479
|
|
|
|
|
|
|
L |
|
480
|
|
|
|
|
|
|
L |
|
481
|
|
|
|
|
|
|
L |
|
482
|
|
|
|
|
|
|
L |
|
483
|
|
|
|
|
|
|
L |
|
484
|
|
|
|
|
|
|
L |
|
485
|
|
|
|
|
|
|
L |
|
486
|
|
|
|
|
|
|
L |
|
487
|
|
|
|
|
|
|
L |
|
488
|
|
|
|
|
|
|
L |
|
489
|
|
|
|
|
|
|
L |
|
490
|
|
|
|
|
|
|
L |
|
491
|
|
|
|
|
|
|
L |
|
492
|
|
|
|
|
|
|
L |
|
493
|
|
|
|
|
|
|
L |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=head1 AUTHOR |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Lincoln Stein Elincoln.stein@gmail.comE. |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Copyright (c) 2011 Ontario Institute for Cancer Research |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
This package and its accompanying libraries is free software; you can |
|
502
|
|
|
|
|
|
|
redistribute it and/or modify it under the terms of the GPL (either |
|
503
|
|
|
|
|
|
|
version 1, or at your option, any later version) or the Artistic |
|
504
|
|
|
|
|
|
|
License 2.0. Refer to LICENSE for the full license text. In addition, |
|
505
|
|
|
|
|
|
|
please see DISCLAIMER.txt for disclaimers of warranty. |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=cut |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
1; |
|
510
|
|
|
|
|
|
|
|