1package VM::EC2::Volume;
2
3=head1 NAME
4
5VM::EC2::Volume - Object describing an Amazon EBS volume
6
7=head1 SYNOPSIS
8
9  use VM::EC2;
10
11  $ec2       = VM::EC2->new(...);
12  @vol = $ec2->describe_volumes;
13  for my $vol (@vols) {
14    $id    = $vol->volumeId;
15    $size  = $vol->size;
16    $snap  = $vol->snapshotId;
17    $zone  = $vol->availabilityZone;
18    $status = $vol->status;
19    $ctime = $vol->createTime;
20    @attachments = $vol->attachments;
21    $attachment  = $vol->attachment;
22    $origin      = $vol->from_snapshot;
23    @snapshots   = $vol->to_snapshots;
24  }
25  $vols[0]->attach('i-12345','/dev/sdg1');
26  $vols[0]->deleteOnTermination('true');
27  $vols[0]->detach;
28  $vols[0]->create_snapshot('automatic snapshot')
29
30=head1 DESCRIPTION
31
32This object is used to describe an Amazon EBS volume. It is returned
33by VM::EC2->describe_volumes().
34
35=head1 METHODS
36
37The following object methods are supported:
38
39 volumeId         -- ID of this volume.
40 size             -- Size of this volume (in GB).
41 snapshotId       -- ID of snapshot this volume was created from.
42 availabilityZone -- Availability zone in which this volume resides.
43 status           -- Volume state, one of "creating", "available",
44                     "in-use", "deleting", "deleted", "error"
45 createTime       -- Timestamp for when volume was created.
46 volumeType       -- The volume type, one of "standard", "io1", or "gp2"
47 iops             -- The number of I/O operations per second that the volume
48                     supports, an integer between 100 and 4000. Only valid for
49                     volumes of type "io1".
50 encrypted        -- True if volume is encrypted.
51 tags             -- Hashref containing tags associated with this group.
52                     See L<VM::EC2::Generic>.
53
54In addition, this class provides several convenience functions:
55
56=head2 $attachment  = $vol->attachment
57
58=head2 @attachments = $vol->attachments
59
60The attachment() method returns a
61VM::EC2::BlockDevice::Attachment object describing the
62attachment of this volume to an instance. If the volume is unused,
63then this returns undef.
64
65The attachments() method is similar, except that it returns a list of
66the attachments.  Currently an EBS volume can only be attached to one
67instance at a time, but the Amazon call syntax supports multiple
68attachments and this method is provided for future compatibility.
69
70=head2 $attachment = $vol->attach($instance,$device)
71
72=head2 $attachment = $vol->attach(-instance_id=>$instance,-device=>$device)
73
74Attach this volume to an instance using virtual device $device. Both
75arguments are required. The result is a
76VM::EC2::BlockDevice::Attachment object which you can monitor by
77calling current_status():
78
79    my $a = $volume->attach('i-12345','/dev/sdg');
80    while ($a->current_status ne 'attached') {
81       sleep 2;
82    }
83    print "volume is ready to go\n";
84
85=head2 $attachment = $volume->detach()
86
87=head2 $attachment = $volume->detach(-instance_id=>$instance_id,
88                                  -device=>$device,
89                                  -force=>$force);
90
91Detaches this volume. With no arguments, will detach the volume from
92whatever instance it is currently attached to. Provide -instance_id
93and/or -device as a check that you are detaching the volume from the
94expected instance and device.
95
96Optional arguments:
97
98 -instance_id    -- ID of the instance to detach from.
99 -device         -- How the device is exposed to the instance.
100 -force          -- Force detachment, even if previous attempts were
101                    unsuccessful.
102
103The result is a VM::EC2::BlockDevice::Attachment object which
104you can monitor by calling current_status():
105
106    my $a = $volume->detach;
107    while ($a->current_status ne 'detached') {
108       sleep 2;
109    }
110    print "volume is ready to go\n";
111
112=head2 $boolean = $vol->deleteOnTermination([$boolean])
113
114Get or set the deleteOnTermination flag for attached volumes. If the volume
115is unattached, then this causes a fatal error. Called with no arguments, this
116method returns the current state of the deleteOnTermination flag for the
117volume's attachment. Called with a true/false argument, the method sets the
118flag by calling modify_instance_attributes() on the corresponding instance
119and returns true if successful.
120
121=head2 $snap = $vol->from_snapshot
122
123Returns the VM::EC2::Snapshot object that this volume was
124originally derived from. It will return undef if the resource no
125longer exists, or if the volume was created from scratch.
126
127=head2 @snap = $vol->to_snapshots
128
129If this volume has been used to create one or more snapshots, this
130method will return them as a list of VM::EC2::Snapshot objects.
131
132=head2 $snapshot = $vol->create_snapshot('Description')
133
134Create a snapshot of the volume and return a VM::EC2::Snapshot
135object. To ensure a consistent snapshot, you should unmount the volume
136before snapshotting it. The optional argument allows you to add a description to the snapshot.
137
138Here is an example:
139
140  $s = $volume->create_snapshot("Backed up at ".localtime);
141  while ($s->current_status eq 'pending') {
142     print "Progress: ",$s->progress,"% done\n";
143  }
144  print "Snapshot status: ",$s->current_status,"\n";
145
146=head2 $status = $vol->current_status
147
148This returns the up-to-date status of the volume. It works by calling
149refresh() and then returning status().
150
151=head2 $boolean = $vol->auto_enable_io([$new_boolean])
152
153Get or set the auto-enable IO flag.
154
155=head2 $boolean = $vol->enable_volume_io()
156
157Enable volume I/O after it has been disabled by an Amazon health
158check. If successful, the call will return true.
159
160=head1 STRING OVERLOADING
161
162When used in a string context, this object will interpolate the
163volumeId.
164
165=head1 SEE ALSO
166
167L<VM::EC2>
168L<VM::EC2::Generic>
169L<VM::EC2::Snapshot>
170L<VM::EC2::BlockDevice>
171L<VM::EC2::BlockDevice::Attachment>
172
173=head1 AUTHOR
174
175Lincoln Stein E<lt>lincoln.stein@gmail.comE<gt>.
176
177Copyright (c) 2011 Ontario Institute for Cancer Research
178
179This package and its accompanying libraries is free software; you can
180redistribute it and/or modify it under the terms of the GPL (either
181version 1, or at your option, any later version) or the Artistic
182License 2.0.  Refer to LICENSE for the full license text. In addition,
183please see DISCLAIMER.txt for disclaimers of warranty.
184
185=cut
186
187use strict;
188use base 'VM::EC2::Generic';
189use VM::EC2::BlockDevice::Attachment;
190use VM::EC2::ProductCode;
191use Carp 'croak';
192
193sub valid_fields {
194    my $self = shift;
195    return qw(volumeId size snapshotId availabilityZone status
196              createTime attachmentSet volumeType iops encrypted tagSet);
197}
198
199sub primary_id {shift->volumeId}
200
201sub attachment {
202    my $self = shift;
203    my $attachments = $self->attachmentSet or return;
204    my $id = $attachments->{item}[0]       or return;
205    return VM::EC2::BlockDevice::Attachment->new($id,$self->aws)
206}
207
208sub attachments {
209    my $self = shift;
210    my $attachments = $self->attachmentSet         or return;
211    my $items       = $self->attachmentSet->{item} or return;
212    my @a = map {VM::EC2::BlockDevice::Attachment->new($_,$self->aws)} @$items;
213    return @a;
214}
215
216sub deleteOnTermination {
217    my $self = shift;
218    $self->refresh;
219    my $attachment = $self->attachment or croak "$self is not attached";
220    return $attachment->deleteOnTermination(@_);
221}
222
223sub from_snapshot {
224    my $self = shift;
225    my $sid  = $self->snapshotId or return;
226    return $self->aws->describe_snapshots(-filter=>{'snapshot-id' => $sid});
227}
228
229sub to_snapshots {
230    my $self = shift;
231    return $self->aws->describe_snapshots(-filter=>{'volume-id' => $self->volumeId});
232}
233
234sub create_snapshot {
235    my $self = shift;
236    my $description = shift;
237    my @param = (-volume_id=>$self->volumeId);
238    push @param,(-description=>$description) if defined $description;
239    return $self->aws->create_snapshot(@param);
240}
241
242sub attach {
243    my $self = shift;
244    my %args;
245    if (@_==2 && $_[0] !~ /^-/) {
246	@args{'-instance_id','-device'} = @_;
247    } else {
248	%args = @_;
249    }
250    $args{-instance_id} && $args{-device}
251       or croak "usage: \$vol->attach(\$instance_id,\$device)";
252    $args{-volume_id} = $self->volumeId;
253    return $self->aws->attach_volume(%args);
254}
255
256sub detach {
257    my $self = shift;
258    return $self->aws->detach_volume(-volume_id=>$self->volumeId,@_);
259}
260
261sub current_status {
262    my $self = shift;
263    $self->refresh;
264    $self->status;
265}
266
267sub current_status_async {
268    my $self = shift;
269    my $to_caller = VM::EC2->condvar;
270
271    my $cv = $self->aws->describe_volumes_async($self->volumeId);
272
273    $cv->cb(sub {
274	my $i = shift->recv;
275	if ($i) {
276	    $to_caller->send($i->status);
277	} else {
278	    $to_caller->send;
279	}
280	    });
281
282    return $to_caller;
283}
284
285
286sub refresh {
287    my $self = shift;
288    local $self->aws->{raise_error} = 1;
289    my $v    = $self->aws->describe_volumes($self->volumeId);
290    %$self   = %$v if $v;
291    return defined $v;
292}
293
294sub auto_enable_io {
295    my $self = shift;
296    return $self->aws->modify_volume_attribute($self,
297					       -auto_enable_io => shift) if @_;
298    return $self->aws->describe_volume_attribute($self,'autoEnableIO');
299}
300
301sub enable_io {
302    my $self = shift;
303    $self->aws->enable_volume_io($self);
304}
305
306sub product_codes {
307    my $self = shift;
308    my @codes = $self->aws->describe_volume_attribute($self,'productCodes');
309    return map {VM::EC2::ProductCode->new($_,$self->aws)} @codes;
310}
311
312sub encrypted {
313    my $self = shift;
314    my $enc = $self->SUPER::encrypted;
315    return $enc eq 'true';
316}
317
3181;
319