VM::EC...ispatch(3pm)
Okay bestie, grab your iced coffee because we're diving into VM::EC2::Dispatch π from the User Contributed Perl Documentation, section 3pm β and it's a whole saga, so it's split into 2 parts. This is part 1, so no sign-off, we're staying seated π«Άβ¨
β¨ NAME β¨
VM::EC2::Dispatch β Create Perl objects from AWS XML requests
π¦ SYNOPSIS π¦
use VM::EC2;
VM::EC2::Dispatch->register('DescribeRegions'=>\&mysub);
VM::EC2::Dispatch->replace('DescribeRegions'=>'My::Type');
sub mysub {
my ($parsed_xml_object,$ec2) = @_;
my $payload = $parsed_xml_object->{regionInfo}
return My::Type->new($payload,$ec2);
}
πΈ DESCRIPTION πΈ
This class is the whole glow-up pipeline: it takes the XML that AWS hands back and turns it into actual Perl objects. Honestly? Iconic. π
Of everything in here, there's basically one method you as a developer are ever gonna care about β the replace() class method. That's the one that lets you swap out the handlers used to map a response onto objects. Main character energy, periodt.
π VM::EC2::Dispatch->replace($request_name => &sub)
π VM::EC2::Dispatch->replace($request_name => 'Class::Name')
π VM::EC2::Dispatch->replace($request_name => 'method_name,arg1,arg2,...')
So before you fire off a VM::EC2 request that you wanna customize, you call replace() with two arguments. Arg one is the name of the request you're customizing β something like "DescribeVolumes". Arg two is either a code reference, a VM::EC2::Dispatch method name plus its arguments (comma-separated), or a class name. Pick your fighter π
If arg two is a code reference, your sub gets invoked with four arguments: the parsed XML response, the VM::EC2 object, the XML namespace string from the request, and the Amazon-assigned request ID. Real talk though, only the first two are actually useful. π
If arg two is a string containing a classname, that class gets loaded if it needs loading, and then its new() method is invoked like this:
Your::Class->new($parsed_xml,$ec2,$xmlns,$requestid)
Your new() method should return one or more objects. The move here is to subclass VM::EC2::Generic and lean on the inherited new() method to stash the parsed XML and the EC2 object. For a simple little template, go peek at the code for VM::EC2::AvailabilityRegion. β¨
If arg two is neither a code reference nor a classname, it gets treated as a VM::EC2::Dispatch method name plus its arguments, separated by commas. The method gets invoked like this:
$dispatch->$method_name($raw_xml,$ec2,$arg1,$arg2,$arg3,...)
There are two methods currently defined for exactly this purpose β boolean(), and fetch_items() β and they handle the preprocessing of several common XML representations of EC2 data. Heads up though: in this form, the RAW XML is what gets passed in, not the parsed data structure. Don't get it twisted. β οΈ
The parsed XML response comes from the XML::Simple module, using these options:
$parser = XML::Simple->new(ForceArray => ['item', 'member'],
KeyAttr => ['key'],
SuppressEmpty => undef);
$parsed = $parser->XMLin($raw_xml)
Generally you're gonna get a hash of hashes out of this. Any tag named 'item' or 'member' gets forced to point at an array reference, and any tag named "key" gets flattened exactly the way the XML::Simple docs describe.
Wanna peek at the raw parsed XML? Easy. Just call any VM::EC2::Object's as_string method:
my ($i) = $ec2->describe_instances;
print $i->as_string;
That hands you a Data::Dumper representation of the XML after parsing. And for a million examples of how all this works, go look at the calls to VM::EC2::Dispatch->register() scattered through the various VM/EC2/REST/*.pm modules. π§
Little lore drop: thereplace()method used to be calledadd_override()in older versions of this module.add_override()is still recognized as an alias for backward compatibility. Glow-up, but the old name still shows up to the party. π
π VM::EC2::Dispatch->register($request_name1 => &sub1,$request_name2 => &sub2,...)
Basically replace()'s cousin β but if the request name is already registered, it does not overwrite it. And yes, you can hand it multiple request=>handler pairs at once. Bulk ordering, we love to see it. ποΈ
π§ OBJECT CREATION METHODS π§
These methods do some simple pre-processing of the parsed XML (again, a hash of hashes) before passing the modified data structure off to the designated object class. They're what you feed in as the second argument to VM::EC2::Dispatch->register(). Let's meet them π
π $bool = $dispatch->boolean($raw_xml,$ec2,$tag)
This one's for XML responses that look like this:
<DeleteVolumeResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<return>true</return>
</DeleteVolumeResponse>
It goes looking inside the structure for the tag named $tag (that's "return" if you didn't provide one), and returns a true value if the contents equal "true".
Pass it to replace() like this:
VM::EC2::Dispatch->replace(DeleteVolume => 'boolean,return';
or, since "return" is the default tag:
VM::EC2::Dispatch->replace(DeleteVolume => 'boolean';
π @list = $dispatch->elb_member_list($raw_xml,$ec2,$tag)
This one handles XML responses from the ELB API, like this lil guy:
<DisableAvailabilityZonesForLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2011-11-15/">
<DisableAvailabilityZonesForLoadBalancerResult>
<AvailabilityZones>
<member>us-west-2a</member>
<member>us-west-2b</member>
</AvailabilityZones>
</DisableAvailabilityZonesForLoadBalancerResult>
<ResponseMetadata>
<RequestId>02eadcfc-fc38-11e1-a1bf-9de31EXAMPLE</RequestId>
</ResponseMetadata>
</DisableAvailabilityZonesForLoadBalancerResponse>
It looks inside the Result structure for the tag named $tag and returns the list wrapped in member elements. Here the tag is 'AvailabilityZones' and the return value would be: ( 'us-west-2a', 'us-west-2b' )
If $embedded_tag gets passed, then it's used for XML responses like this one, where the member list has an embedded tag:
<RegisterInstancesWithLoadBalancerResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2011-11-15/">
<RegisterInstancesWithLoadBalancerResult>
<Instances>
<member>
<InstanceId>i-12345678</InstanceId>
</member>
<member>
<InstanceId>i-90abcdef</InstanceId>
</member>
</Instances>
</RegisterInstancesWithLoadBalancerResult>
<ResponseMetadata>
<RequestId>f4f12596-fc3b-11e1-be5a-f71ecEXAMPLE</RequestId>
</ResponseMetadata>
</RegisterInstancesWithLoadBalancerResponse>
It looks inside the Result structure for the tag named $tag and returns the list wrapped in a member element plus the embedded tag. Here the tag is 'Instances', the embedded tag is 'InstanceId', and the return value would be: ( 'i-12345678', 'i-90abcdef' )
π¦ fetch_items($raw_xml,$ec2,$container_tag,$object_class,$nokey)
@objects = $dispatch->fetch_items($raw_xml,$ec2,$container_tag,$object_class,$nokey)
Used for XML responses like this:
<DescribeKeyPairsResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<keySet>
<item>
<keyName>gsg-keypair</keyName>
<keyFingerprint>
1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f
</keyFingerprint>
</item>
<item>
<keyName>default-keypair</keyName>
<keyFingerprint>
0a:93:bb:e8:c2:89:e9:d8:1f:42:5d:37:1d:8d:b8:0a:88:f1:f1:1a
</keyFingerprint>
</item>
</keySet>
</DescribeKeyPairsResponse>
It looks inside the structure for the tag named $container_tag, pulls out the items stored under <item>, and then passes the parsed contents to $object_class->new(). The optional $nokey argument suppresses XML::Simple's default flattening behaviour β you know, the thing where it turns tags named "key" into hash keys. Bye to that π«
Pass it to replace() like this:
VM::EC2::Dispatch->replace(DescribeVolumes => 'fetch_items,volumeSet,VM::EC2::Volume')
π @objects = $dispatch->fetch_members($raw_xml,$ec2,$container_tag,$object_class,$nokey)
Used for XML responses from ELB API calls β the ones that contain a key which is the name of the API call with 'Result' appended. All of these XML responses use 'member' as the item delimiter instead of 'item'. Different delimiter, same dream β¨
π @objects = $dispatch->fetch_rds_objects($raw_xml,$ec2,$container_tag,$object_class,$nokey)
Used for XML responses from RDS API calls β which, same as above, contain a key that's the name of the API call with 'Result' appended. On top of that, the structure is a list of objects wrapped in a plural version of the object's name. RDS really said "let me do it differently" π
π @objects = $dispatch->fetch_items_iterator($raw_xml,$ec2,$container_tag,$object_class,$token_name)
This one's for requests that have a -max_results argument. In that case the response is gonna have a nextToken field, which you can use to go fetch the "next page" of results. Pagination era π
The $token_name is some unique identifying token. It gets turned into two temporary EC2 instance variables: one named ${token_name}_token, which holds the nextToken value, and the other named ${token_name}_stop, which flags the caller that no more results are coming. The door is closed bestie πͺ
This all has to be coordinated with the request subroutine. Go see how describe_instance_status() and describe_spot_price_history() pull it off. π
π EXAMPLE OF USING OVERRIDE TO SUBCLASS VM::EC2::Volume π
So the author decided a volume object shouldn't be able to delete itself. Youβ¦ disagree with that decision. Respectfully. So let's subclass VM::EC2::Volume and add a delete() method. We're taking matters into our own hands πͺ
First, subclass the VM::EC2::Volume class:
package MyVolume;
use base 'VM::EC2::Volume';
sub delete {
my $self = shift;
$self->ec2->delete_volume($self);
}
Now subclass VM::EC2 to add the appropriate overrides to the new() method:
package MyEC2;
use base 'VM::EC2';
sub new {
my $class = shift;
VM::EC2::Dispatch->replace(CreateVolume =>'MyVolume');
VM::EC2::Dispatch->replace(DescribeVolumes=>'fetch_items,volumeSet,MyVolume');
return $class->SUPER::new(@_);
}
Now we can test it out:
use MyEC2;
# find all volumes that are "available" and not in-use
my @vol = $ec2->describe_volumes({status=>'available'});
for my $vol (@vol) {
$vol->delete && print "$vol deleted\n"
}
Slay. It works. π
π SEE ALSO π
VM::EC2VM::EC2::ObjectVM::EC2::GenericVM::EC2::BlockDeviceVM::EC2::BlockDevice::AttachmentVM::EC2::BlockDevice::MappingVM::EC2::BlockDevice::Mapping::EBSVM::EC2::ErrorVM::EC2::GenericVM::EC2::GroupVM::EC2::ImageVM::EC2::InstanceVM::EC2::Instance::ConsoleOutputVM::EC2::Instance::SetVM::EC2::Instance::StateVM::EC2::Instance::State::ChangeVM::EC2::Instance::State::ReasonVM::EC2::RegionVM::EC2::ReservationSetVM::EC2::SecurityGroupVM::EC2::SnapshotVM::EC2::TagVM::EC2::Volume
π AUTHOR π
Okay bestie, shoutout time π€β¨ this whole thing was written by Lincoln Stein β you can reach him at lincoln.stein@gmail.com if you wanna send fan mail or, like, a bug report π
The legal tea, and it's actually good news for once:
- Copyright (c) 2011 Ontario Institute for Cancer Research π
- This package and its accompanying libraries is free software β so you can redistribute it and/or modify it under the terms of the GPL (either version 1, or at your option, any later version) or the Artistic License 2.0. Pick your fighter, both are valid π
- Wanna read the whole thing? Peep
LICENSEfor the full license text, no cap π - And alsoooo, please go look at
DISCLAIMER.txtfor the disclaimers of warranty β yes, that means what you think it means, no promises were made π
fr fr, read the fine print, it's giving "legally required but also genuinely useful" π«Ά
And that's a wrap on the whole page, bestie!! π This edition came to you courtesy of perl v5.38.2, dated 2024-08-10, and it's the VM::EC...ispatch(3pm) page β slay, you read the entire thing, main character energy for real πβπ
π¦ where this page comes from
- debian trixie: libvm-ec2-perl 1.28-5 Β· Artistic-2.0 OR GPL-1.0-or-later
- ubuntu resolute: libvm-ec2-perl 1.28-5 Β· Artistic-2.0 OR GPL-1.0-or-later
- ubuntu stonking: libvm-ec2-perl 1.28-5 Β· Artistic-2.0 OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms