File Coverage

blib/lib/NetApp/Snapshot.pm
Criterion Covered Total %
statement 31 188 16.4
branch 0 62 0.0
condition 0 6 0.0
subroutine 10 24 41.6
pod 4 6 66.6
total 45 286 15.7


line stmt bran cond sub pod time code
1              
2             package NetApp::Snapshot;
3              
4             our $VERSION = '500.002';
5             $VERSION = eval $VERSION; ## no critic: StringyEval
6              
7 1     1   753 use strict;
  1         2  
  1         44  
8 1     1   6 use warnings;
  1         2  
  1         34  
9 1     1   5 use English;
  1         4  
  1         10  
10 1     1   590 use Carp;
  1         2  
  1         86  
11              
12 1     1   6 use Class::Std;
  1         2  
  1         7  
13 1     1   132 use Params::Validate qw( :all );
  1         3  
  1         214  
14 1     1   7 use Regexp::Common;
  1         2  
  1         11  
15              
16 1     1   1552 use NetApp::Snapshot::Delta;
  1         4  
  1         42  
17 1     1   623 use NetApp::Snapshot::Schedule;
  1         4  
  1         3294  
18              
19             {
20              
21             my %parent_of :ATTR( get => 'parent' );
22              
23             my %name_of :ATTR( get => 'name' );
24             my %date_of :ATTR( get => 'date' );
25             my %used_of :ATTR( get => 'used' );
26             my %total_of :ATTR( get => 'total' );
27              
28             sub BUILD {
29              
30 0     0 0 0 my ($self,$ident,$args_ref) = @_;
31              
32 0         0 my @args = %$args_ref;
33              
34 0         0 my (%args) = validate( @args, {
35             parent => { type => OBJECT },
36             name => { type => SCALAR },
37             date => { type => SCALAR },
38             used => { type => SCALAR },
39             total => { type => SCALAR },
40             });
41              
42 0         0 $parent_of{$ident} = $args{parent};
43 0         0 $name_of{$ident} = $args{name};
44 0         0 $date_of{$ident} = $args{date};
45 0         0 $used_of{$ident} = $args{used};
46 0         0 $total_of{$ident} = $args{total};
47              
48             }
49              
50             sub get_filer {
51 0     0 0 0 return shift->get_parent->get_filer;
52             }
53              
54             sub get_snapshot_deltas {
55              
56 0     0 1 0 my $self = shift;
57              
58 0         0 return $self->_get_snapshot_deltas(
59             parent => $self->get_parent,
60             from => $self,
61             );
62              
63             }
64              
65             sub get_reclaimable {
66              
67 0     0 1 0 my $self = shift;
68              
69 0 0       0 if ( $self->get_parent->isa("NetApp::Aggregate") ) {
70 0         0 croak("Aggregate snapshots do not support 'snap reclaimable'\n");
71             }
72              
73             $self->get_filer->_run_command(
74 0         0 command => [ qw( snap reclaimable ),
75             $self->get_parent->get_name,
76             $self->get_name ],
77             nonfatal => 1,
78             );
79              
80 0         0 my @stdout = $self->get_filer->_get_command_stdout;
81 0         0 my @stderr = $self->get_filer->_get_command_stderr;
82              
83 0         0 while ( defined (my $line = shift @stdout) ) {
84 0 0       0 if ( $line =~ /Approximately (\d+)/ ) {
85 0         0 return $1;
86             }
87             }
88              
89             carp(
90 0         0 "Unable to determine reclaimable space for ",
91             $self->get_parent->get_name, ":",
92             $self->get_name, "\n",
93             @stderr,
94             );
95              
96 0         0 return undef;
97              
98             }
99              
100             sub restore {
101              
102 0     0 1 0 my $self = shift;
103              
104 0         0 my (%args) = validate( @_, {
105             type => { type => SCALAR,
106             regexp => qr{^(vol|file)$},
107             default => 'vol',
108             optional => 1 },
109             from_path => { type => SCALAR,
110             optional => 1 },
111             to_path => { type => SCALAR,
112             optional => 1 },
113             });
114              
115 0 0 0     0 if ( $args{type} eq 'file' && ! $args{from_path} ) {
116 0         0 croak(
117             "Missing required argment 'from_path'\n",
118             "File restores must specify the from_path\n",
119             );
120             }
121              
122 0 0 0     0 if ( $args{type} eq 'vol' && $args{to_path} ) {
123 0         0 croak(
124             "Invalid argument 'to_path'\n",
125             "Volume restores can not specify to_path\n",
126             );
127             }
128              
129 0         0 my @command = qw( snap restore );
130              
131 0 0       0 if ( $self->get_parent->isa("NetApp::Aggregate" ) ) {
132 0         0 push @command, '-A';
133             }
134              
135 0 0       0 if ( $args{to_path} ) {
136 0         0 push @command, '-r', $args{to_path};
137             }
138              
139 0         0 push @command, (
140             qw( -f -s ), $self->get_name,
141             qw( -t ), $args{type},
142             );
143              
144 0 0       0 if ( $args{type} eq 'vol' ) {
145 0         0 push @command, $self->get_parent->get_name;
146             } else {
147 0         0 push @command, $args{from_path};
148             }
149              
150 0         0 return $self->get_filer->_run_command(
151             command => \@command,
152             );
153              
154             }
155              
156             sub rename {
157              
158 0     0 1 0 my $self = shift;
159 0         0 my $ident = ident $self;
160              
161 0         0 my ($newname) = validate_pos(
162             @_,
163             { type => SCALAR },
164             );
165              
166 0         0 my @command = qw( snap rename );
167              
168 0 0       0 if ( $self->get_parent->isa("NetApp::Aggregate") ) {
169 0         0 push @command, '-A';
170             }
171              
172 0         0 push @command, $self->get_name, $newname;
173              
174 0         0 $self->_run_command(
175             command => \@command,
176             );
177              
178 0         0 $name_of{$ident} = $newname;
179              
180 0         0 return 1;
181              
182             }
183              
184             }
185              
186             # NOTE: These are class methods, since you can request snapshots,
187             # deltas, etc from an aggregate, volume, or a specific snapshot.
188              
189             sub _get_snapshots {
190              
191 0     0   0 my $class = shift;
192              
193 0         0 my (%args) = validate( @_, {
194             parent => { type => OBJECT },
195             });
196              
197 0         0 my $parent = $args{parent};
198              
199 0         0 my @command = qw( snap list );
200              
201 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
202 0         0 push @command, '-A';
203             }
204              
205 0         0 push @command, $parent->get_name;
206              
207 0         0 $parent->get_filer->_run_command(
208             command => \@command,
209             );
210              
211 0         0 my @stdout = $parent->get_filer->_get_command_stdout;
212              
213 0         0 my @snapshots = ();
214              
215 0         0 while ( defined (my $line = shift @stdout) ) {
216              
217 0 0       0 next if $line =~ /^(Volume|Aggregate)/;
218 0 0       0 next if $line =~ /^working/;
219 0 0       0 next if $line =~ /^\s*$/;
220              
221 0 0       0 last if $line =~ /No snapshots exist/;
222              
223 0 0       0 next if $line =~ m:^\s*%/used:;
224 0 0       0 next if $line =~ /^-+/;
225              
226 0         0 my $snapshot = $class->_parse_snap_list( $line );
227              
228 0         0 push @snapshots, NetApp::Snapshot->new({
229             parent => $parent,
230             %$snapshot,
231             });
232              
233             }
234              
235 0         0 return @snapshots;
236              
237             }
238              
239             sub _create_snapshot {
240              
241 0     0   0 my $class = shift;
242              
243 0         0 my (%args) = validate( @_, {
244             parent => { type => OBJECT },
245             name => { type => SCALAR },
246             });
247              
248 0         0 my $parent = $args{parent};
249              
250 0         0 my @command = qw( snap create );
251              
252 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
253 0         0 push @command, '-A';
254             }
255              
256 0         0 push @command, $args{name};
257              
258 0         0 return $parent->get_filer->_run_command(
259             command => \@command,
260             );
261              
262             }
263              
264             sub _delete_snapshot {
265              
266 0     0   0 my $class = shift;
267              
268 0         0 my (%args) = validate( @_, {
269             parent => { type => OBJECT },
270             name => { type => SCALAR },
271             });
272              
273 0         0 my $parent = $args{parent};
274              
275 0         0 my @command = qw( snap delete );
276              
277 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
278 0         0 push @command, '-A';
279             }
280              
281 0         0 push @command, $args{name};
282              
283 0         0 return $parent->get_filer->_run_command(
284             command => \@command,
285             );
286              
287             }
288              
289             sub _set_snapshot_schedule {
290              
291 0     0   0 my $class = shift;
292              
293 0         0 my (%args) = validate( @_, {
294             parent => { type => OBJECT },
295             weekly => { type => SCALAR },
296             daily => { type => SCALAR },
297             hourly => { type => SCALAR },
298             hourlist => { type => ARRAYREF,
299             optional => 1 },
300             });
301              
302 0         0 my $parent = $args{parent};
303              
304 0         0 my @command = qw( snap sched );
305              
306 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
307 0         0 push @command, '-A';
308             }
309              
310 0         0 push @command, $parent->get_name, $args{weekly}, $args{daily};
311              
312 0         0 my $hourly = $args{hourly};
313              
314 0 0       0 if ( $args{hourlist} ) {
315 0         0 $hourly .= '@' . join( ',', @{ $args{hourlist} } );
  0         0  
316             }
317              
318 0         0 push @command, $args{hourly};
319              
320 0         0 return $parent->get_filer->_run_command(
321             command => \@command,
322             );
323              
324             }
325              
326             sub _get_snapshot_schedule {
327              
328 0     0   0 my $class = shift;
329              
330 0         0 my (%args) = validate( @_, {
331             parent => { type => OBJECT },
332             });
333              
334 0         0 my $parent = $args{parent};
335              
336 0         0 my @command = qw( snap sched );
337              
338 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
339 0         0 push @command, '-A';
340             }
341              
342 0         0 push @command, $parent->get_name;
343              
344 0         0 $parent->get_filer->_run_command(
345             command => \@command,
346             );
347              
348 0         0 my @stdout = $parent->get_filer->_get_command_stdout;
349              
350 0         0 my $schedule =
351             NetApp::Snapshot::Schedule->_parse_snap_sched( shift @stdout );
352              
353 0         0 return NetApp::Snapshot::Schedule->new({
354             parent => $parent,
355             %$schedule,
356             });
357              
358             }
359              
360             sub _set_snapshot_reserved {
361              
362 0     0   0 my $class = shift;
363              
364 0         0 my (%args) = validate( @_, {
365             parent => { type => OBJECT },
366             reserved => { type => SCALAR },
367             });
368              
369 0         0 my $parent = $args{parent};
370 0         0 my $reserved = $args{reserved};
371              
372 0         0 my @command = qw( snap reserve );
373              
374 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
375 0         0 push @command, '-A';
376             }
377              
378 0         0 push @command, $parent->get_name, $reserved;
379              
380 0         0 return $parent->get_filer->_run_command(
381             command => \@command,
382             );
383              
384             }
385              
386             sub _get_snapshot_reserved {
387              
388 0     0   0 my $class = shift;
389              
390 0         0 my (%args) = validate( @_, {
391             parent => { type => OBJECT },
392             });
393              
394 0         0 my $parent = $args{parent};
395 0         0 my $parent_class = ref $parent;
396              
397 0         0 my @command = qw( snap reserve );
398              
399 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
400 0         0 push @command, '-A';
401             }
402              
403 0         0 push @command, $parent->get_name;
404              
405 0         0 $parent->get_filer->_run_command(
406             command => \@command,
407             );
408              
409 0         0 my @stdout = $parent->get_filer->_get_command_stdout;
410              
411 0         0 my $line = shift @stdout;
412              
413 0 0       0 if ( $line =~ /reserve is (\d+)%/ ) {
414 0         0 return $1;
415             } else {
416 0         0 croak(
417             "Unable to determine snapshot reserve for $parent_class",
418             $parent->get_name, "\n",
419             );
420             }
421              
422             }
423              
424             sub _get_snapshot_deltas {
425              
426 0     0   0 my $class = shift;
427              
428 0         0 my (%args) = validate( @_, {
429             parent => { type => OBJECT },
430             from => { isa => 'NetApp::Snapshot',
431             optional => 1 },
432             to => { isa => 'NetApp::Snapshot',
433             depends => [qw( from )],
434             optional => 1 },
435             });
436              
437 0         0 my $parent = $args{parent};
438              
439 0         0 my @command = qw( snap delta );
440              
441 0 0       0 if ( $parent->isa("NetApp::Aggregate") ) {
442 0         0 push @command, '-A';
443             }
444              
445 0         0 push @command, $parent->get_name;
446              
447 0 0       0 if ( $args{from} ) {
448 0         0 push @command, $args{from}->get_name;
449             }
450              
451 0 0       0 if ( $args{to} ) {
452 0         0 push @command, $args{to}->get_name;
453             }
454              
455             $parent->get_filer->_run_command(
456 0         0 command => \@command,
457             );
458              
459 0         0 my @stdout = $parent->get_filer->_get_command_stdout;
460              
461 0         0 my @deltas = ();
462              
463 0         0 my $summary = 0;
464              
465 0         0 while ( defined( my $line = shift @stdout) ) {
466              
467 0 0       0 next if $line =~ /^\s*$/;
468 0 0       0 next if $line =~ /^(Volume|Aggregate|working|From)/;
469 0 0       0 next if $line =~ /^[-\s]+$/;
470              
471 0 0       0 last if $line =~ /No snapshots exist/;
472              
473 0 0       0 if ( $line =~ /^Summary/ ) {
474 0         0 $summary = 1;
475 0         0 next;
476             }
477              
478 0         0 my $delta = NetApp::Snapshot::Delta->_parse_snap_delta( $line );
479              
480 0         0 push @deltas, NetApp::Snapshot::Delta->new({
481             summary => $summary,
482             %$delta,
483             });
484              
485             }
486              
487 0         0 return @deltas;
488              
489             }
490              
491             sub _parse_snap_list {
492              
493 1     1   16 my $class = shift;
494 1         2 my $line = shift;
495              
496 1         18 $line =~ m{ ^ \s* \d+% \s+ \( \s*
497             (\d+)
498             % \) \s+ \d+% \s+ \( \s*
499             (\d+)
500             % \) \s+
501             ( \w+ \s+ \d+ \s+ \d+ : \d+ )
502             \s+
503             (\S+) }x;
504              
505             return {
506 1         14 used => $1,
507             total => $2,
508             date => $3,
509             name => $4,
510             };
511              
512             }
513              
514             1;