File Coverage

blib/lib/NetApp/Aggregate.pm
Criterion Covered Total %
statement 90 239 37.6
branch 18 46 39.1
condition 0 12 0.0
subroutine 14 43 32.5
pod 27 29 93.1
total 149 369 40.3


line stmt bran cond sub pod time code
1              
2             package NetApp::Aggregate;
3              
4             our $VERSION = '500.002';
5             $VERSION = eval $VERSION; ## no critic: StringyEval
6              
7 1     1   711 use strict;
  1         4  
  1         43  
8 1     1   5 use warnings;
  1         2  
  1         34  
9 1     1   5 use English;
  1         2  
  1         9  
10 1     1   558 use Carp;
  1         2  
  1         72  
11              
12 1     1   7 use Class::Std;
  1         2  
  1         8  
13 1     1   123 use Params::Validate qw( :all );
  1         2  
  1         203  
14 1     1   7 use Regexp::Common;
  1         2  
  1         10  
15              
16 1     1   1267 use NetApp::Aggregate::Plex;
  1         4  
  1         65  
17 1     1   573 use NetApp::Aggregate::RAIDGroup;
  1         2  
  1         4091  
18              
19             {
20              
21             my %filer_of :ATTR( get => 'filer' );
22              
23             my %name_of :ATTR( get => 'name' );
24             my %state_of :ATTR;
25             my %status_of :ATTR;
26             my %options_of :ATTR;
27             my %volumes_of :ATTR;
28              
29             my %plex_of :ATTR( get => 'plex' );
30              
31             sub BUILD {
32              
33 0     0 0 0 my ($self,$ident,$args_ref) = @_;
34              
35 0         0 my @args = %$args_ref;
36              
37 0         0 my (%args) = validate( @args, {
38             filer => { isa => 'NetApp::Filer' },
39             name => { type => SCALAR },
40             state => { type => HASHREF },
41             status => { type => HASHREF },
42             options => { type => HASHREF },
43             volumes => { type => HASHREF },
44             plex => { type => HASHREF },
45             });
46              
47 0         0 $filer_of{$ident} = $args{filer};
48 0         0 $name_of{$ident} = $args{name};
49 0         0 $state_of{$ident} = $args{state};
50 0         0 $status_of{$ident} = $args{status};
51 0         0 $options_of{$ident} = $args{options};
52 0         0 $volumes_of{$ident} = $args{volumes};
53              
54 0         0 $plex_of{$ident} =
55             NetApp::Aggregate::Plex->new( $args{plex} );
56              
57             }
58              
59             sub get_states {
60 0     0 1 0 return keys %{ $state_of{ident shift} };
  0         0  
61             }
62              
63             sub get_state {
64            
65 0     0 1 0 my $self = shift;
66 0         0 my $ident = ident $self;
67 0         0 my $state = shift;
68              
69 0         0 return $state_of{$ident}->{$state};
70              
71             }
72              
73             sub get_statuses { # Stati? Oh, hell no...
74 0     0 1 0 return keys %{ $status_of{ident shift} };
  0         0  
75             }
76              
77             sub get_status {
78              
79 0     0 1 0 my $self = shift;
80 0         0 my $ident = ident $self;
81 0         0 my $status = shift;
82              
83 0         0 return $status_of{$ident}->{$status};
84              
85             }
86              
87             sub get_options {
88 0     0 1 0 return keys %{ $options_of{ident shift} };
  0         0  
89             }
90              
91             sub get_option {
92              
93 0     0 1 0 my $self = shift;
94 0         0 my $ident = ident $self;
95 0         0 my $option = shift;
96              
97 0 0       0 if ( exists $options_of{$ident}->{$option} ) {
98 0         0 return $options_of{$ident}->{$option};
99             } else {
100 0         0 return undef;
101             }
102              
103             }
104              
105             sub set_option {
106              
107 0     0 0 0 my $self = shift;
108 0         0 my $option = shift;
109 0         0 my $value = shift;
110              
111 0         0 my $ident = ident $self;
112              
113 0         0 my $name = $self->get_name;
114              
115 0         0 my @command = ( qw(aggr options), $name, $option, $value );
116              
117 0         0 $self->get_filer->_run_command( command => @command );
118              
119 0         0 $options_of{$ident}->{$option} = $value;
120              
121 0         0 return 1;
122              
123             }
124              
125             sub get_volume_names {
126 0     0 1 0 return keys %{ $volumes_of{ident shift} };
  0         0  
127             }
128              
129             sub get_volumes {
130              
131 0     0 1 0 my $self = shift;
132              
133 0         0 my @volumes = ();
134              
135 0         0 foreach my $volume ( $self->get_volume_names ) {
136 0         0 push @volumes, $self->get_filer->get_volume( $volume );
137             }
138              
139 0         0 return @volumes;
140              
141             }
142              
143             sub get_volume {
144            
145 0     0 1 0 my $self = shift;
146 0         0 my $ident = ident $self;
147              
148 0         0 my $name = shift;
149              
150 0 0       0 if ( not exists $volumes_of{$ident}->{$name} ) {
151 0         0 croak(
152             "No such volume $name in aggregate ",
153             $self->get_name, "\n",
154             );
155             }
156              
157 0         0 return $self->get_filer->get_volume( $name );
158              
159             }
160              
161             sub create_volume {
162              
163 0     0 1 0 my $self = shift;
164              
165 0         0 my (%args) = validate( @_, {
166             name => { type => SCALAR },
167             size => { type => SCALAR },
168             space => { type => SCALAR,
169             regexp => qr{^(none|filer|volume)$},
170             optional => 1 },
171             language => { type => SCALAR,
172             optional => 1 },
173             source_filer => { type => SCALAR,
174             depends => [qw( source_volume )],
175             optional => SCALAR },
176             source_folume => { type => SCALAR,
177             depends => [qw( source_filer )],
178             optional => 1 },
179             });
180              
181 0 0 0     0 if ( ref $args{source_filer} &&
182             $args{source_filer}->isa("NetApp::Filer") ) {
183 0         0 $args{source_filer} = $args{source_filer}->get_hostname;
184             }
185              
186 0 0 0     0 if ( ref $args{source_volume} &&
187             $args{source_volume}->isa("NetApp::Volume") ) {
188 0         0 $args{source_volume} = $args{source_volume}->get_name;
189             }
190              
191 0 0 0     0 if ( $args{source_filer} &&
      0        
192             ( $args{space} || $args{language} ) ) {
193 0         0 croak(
194             "Mutually exclusive options: space and/or language may not\n",
195             "be specified when source_filer/source_volume are given.\n",
196             );
197             }
198              
199 0         0 my @command = ( qw( vol create ), $args{name} );
200              
201 0 0       0 if ( $args{language} ) {
202 0         0 push @command, '-l', $args{language};
203             }
204              
205 0 0       0 if ( $args{space} ) {
206 0         0 push @command, '-s', $args{space};
207             }
208              
209 0         0 push @command, $self->get_name, $args{size};
210              
211 0 0       0 if ( $args{source_filer} ) {
212 0         0 push @command, '-S',
213             join( ':', $args{source_filer}, $args{source_volume} );
214             }
215              
216 0         0 $self->get_filer->_run_command( command => \@command );
217              
218 0         0 return $self->get_filer->get_volume( name => $args{name} );
219              
220             }
221              
222             sub destroy_volume {
223              
224 0     0 1 0 my $self = shift;
225 0         0 my $ident = ident $self;
226              
227 0         0 my (%args) = validate( @_, {
228             name => { type => SCALAR },
229             });
230              
231 0         0 my $aggrname = $self->get_name;
232              
233 0 0       0 if ( not $volumes_of{$ident}->{$args{name}} ) {
234 0         0 croak("No such volume $args{name} in aggregate $aggrname\n");
235             }
236              
237             $self->get_filer->_run_command(
238 0         0 command => [qw(vol destroy), $args{name}, '-f'],
239             );
240              
241 0         0 delete $volumes_of{$ident}->{$args{name}};
242              
243 0         0 return 1;
244            
245             }
246              
247             sub get_qtree_names {
248 0     0 1 0 my $self = shift;
249 0         0 return map { $_->get_name } $self->get_qtrees;
  0         0  
250             }
251              
252             sub get_qtree {
253 0     0 1 0 my $self = shift;
254 0         0 my $name = shift;
255 0         0 return $self->get_filer->get_qtree( $name );
256             }
257              
258             sub get_qtrees {
259              
260 0     0 1 0 my $self = shift;
261              
262 0         0 my @qtrees = ();
263              
264 0         0 foreach my $volume ( $self->get_volumes ) {
265 0         0 push @qtrees, $volume->get_qtrees;
266             }
267            
268 0         0 return @qtrees;
269              
270             }
271              
272             sub get_snapshots {
273 0     0 1 0 return NetApp::Snapshot->_get_snapshots( parent => shift );
274             }
275              
276             sub get_snapshot {
277 0     0 1 0 my $self = shift;
278 0         0 my ($name) = validate_pos( @_, { type => SCALAR } );
279 0         0 return grep { $_->get_name eq $name } $self->get_snapshots;
  0         0  
280             }
281              
282             sub create_snapshot {
283 0     0 1 0 my $self = shift;
284 0         0 my ($name) = validate_pos( @_, { type => SCALAR } );
285 0         0 return NetApp::Snapshot->_create_snapshot(
286             parent => $self,
287             name => $name,
288             );
289             }
290              
291             sub delete_snapshot {
292 0     0 1 0 my $self = shift;
293 0         0 my ($name) = validate_pos( @_, { type => SCALAR } );
294 0         0 return NetApp::Snapshot->_delete_snapshot(
295             parent => $self,
296             name => $name,
297             );
298             }
299              
300             sub get_snapshot_deltas {
301 0     0 1 0 return NetApp::Snapshot->_get_snapshot_deltas( parent => shift );
302             }
303              
304             sub get_snapshot_reserved {
305 0     0 1 0 return NetApp::Snapshot->_get_snapshot_reserved( parent => shift );
306             }
307              
308             sub set_snapshot_reserved {
309 0     0 1 0 my $self = shift;
310 0         0 my ($reserved) = validate_pos( @_, { type => SCALAR } );
311 0         0 return NetApp::Snapshot->_set_snapshot_reserved(
312             parent => $self,
313             reserved => $reserved,
314             );
315             }
316              
317             sub get_snapshot_schedule {
318 0     0 1 0 return NetApp::Snapshot->_get_snapshot_schedule(
319             parent => shift,
320             @_
321             );
322             }
323              
324             sub set_snapshot_schedule {
325 0     0 1 0 return NetApp::Snapshot->_set_snapshot_schedule(
326             parent => shift,
327             @_
328             );
329             }
330              
331             sub rename {
332              
333 0     0 1 0 my $self = shift;
334 0         0 my $ident = ident $self;
335              
336 0         0 my (%args) = validate( @_, {
337             newname => { type => SCALAR },
338             });
339              
340 0         0 my $oldname = $self->get_name;
341              
342 0         0 $self->get_filer->_run_command(
343             command => [qw(aggr rename), $oldname, $args{newname}],
344             );
345              
346 0         0 $name_of{$ident} = $args{newname};
347              
348 0         0 return 1;
349              
350             }
351              
352             sub offline {
353              
354 0     0 1 0 my $self = shift;
355 0         0 my $ident = ident $self;
356              
357 0         0 my (%args) = validate( @_, {
358             cifsdelaytime => { type => SCALAR,
359             optional => 1 },
360             });
361              
362 0         0 my @command = ( qw(aggr offline), $self->get_name );
363              
364 0 0       0 if ( $args{cifsdelaytime} ) {
365 0         0 push @command, '-t', $args{cifsdelaytime};
366             }
367              
368             $self->get_filer->_run_command(
369 0         0 command => \@command,
370             );
371              
372 0         0 delete $state_of{$ident}->{online};
373 0         0 delete $state_of{$ident}->{restricted};
374 0         0 $state_of{$ident}->{offline} = 1;
375            
376 0         0 return 1;
377              
378             }
379              
380             sub online {
381              
382 0     0 1 0 my $self = shift;
383 0         0 my $ident = ident $self;
384              
385 0         0 my (%args) = validate( @_, {
386             force => { type => SCALAR,
387             optional => 1 },
388             });
389              
390 0         0 my @command = ( qw( aggr online ), $self->get_name );
391              
392 0 0       0 if ( $args{force} ) {
393 0         0 push @command, '-f';
394             }
395            
396             $self->get_filer->_run_command(
397 0         0 command => \@command,
398             );
399              
400 0         0 delete $state_of{$ident}->{offline};
401 0         0 delete $state_of{$ident}->{restricted};
402 0         0 $state_of{$ident}->{online} = 1;
403            
404 0         0 return 1;
405              
406             }
407              
408             sub restrict {
409            
410 0     0 1 0 my $self = shift;
411 0         0 my $ident = ident shift;
412              
413 0         0 my (%args) = validate( @_, {
414             cifsdelaytime => { type => SCALAR,
415             optional => 1 },
416             });
417              
418 0         0 my @command = ( qw(aggr restrict), $self->get_name );
419              
420 0 0       0 if ( $args{cifsdelaytime} ) {
421 0         0 push @command, '-t', $args{cifsdelaytime};
422             }
423              
424             $self->get_filer->_run_command(
425 0         0 command => \@command,
426             );
427              
428 0         0 delete $state_of{$ident}->{offline};
429 0         0 delete $state_of{$ident}->{online};
430 0         0 $state_of{$ident}->{restricted} = 1;
431              
432 0         0 return 1;
433              
434             }
435              
436            
437              
438             }
439              
440             # Class methods for parsing aggr command output
441              
442             sub _parse_aggr_status_headers {
443              
444 4     4   2859 my $class = shift;
445 4         7 my $header = shift;
446              
447 4         7 my $indices = {};
448 4         7 my $index = 0;
449              
450 4 100       54 my ($aggr) = ( $header =~ /(^\s+Aggr\s+)/ ) or
451             croak(
452             "Unable to match 'Aggr' column header\n"
453             );
454              
455 3         11 $indices->{aggr} = [ 0, length($aggr) ];
456 3         6 $index += length($aggr);
457              
458 3 100       36 my ($state) = ( $header =~ /(State\s+)/ ) or
459             croak(
460             "Unable to match 'State' column header\n"
461             );
462              
463 2         5 $indices->{state} = [ $index, length($state) ];
464 2         3 $index += length($state);
465              
466 2 100       22 my ($status) = ( $header =~ /(Status\s+)/ ) or
467             croak(
468             "Unable to match 'Status' column header\n"
469             );
470            
471 1         2 $indices->{status} = [ $index, length($status) ];
472 1         3 $index += length($status);
473              
474 1         4 $indices->{options} = [ $index ];
475              
476 1         4 return $indices;
477              
478             }
479              
480             sub _parse_aggr_status_aggregate {
481              
482 2     2   4138 my $class = shift;
483            
484 2         100 my %args = validate( @_, {
485             indices => { type => HASHREF },
486             line => { type => SCALAR },
487             aggregate => { type => HASHREF,
488             default => {},
489             optional => 1 },
490             });
491            
492 2         20 my $indices = $args{indices};
493 2         4 my $aggregate = $args{aggregate};
494 2         4 my $line = $args{line};
495              
496 2 50       8 if ( $line =~ m{Volumes: } ) {
497 0         0 return $aggregate;
498             }
499              
500 2         6 foreach my $column ( qw( aggr state status options ) ) {
501              
502 8         11 my $value = "";
503              
504 8 100       21 if ( $indices->{$column}->[1] ) {
505 6         16 $value = substr( $line,
506             $indices->{$column}->[0],
507             $indices->{$column}->[1] );
508             } else {
509 2         6 $value = substr( $line,
510             $indices->{$column}->[0] );
511             }
512              
513 8         43 $value =~ s/$RE{ws}{crop}//g;
514              
515 8 100       1019 if ( $column eq 'aggr' ) {
516 2 100       8 if ( $value ) {
517              
518 1         3 $aggregate->{name} = $value;
519              
520 1         5 my ($name) = split( /\s+/, $line );
521              
522 1 50       5 if ( length($name) > length($value) ) {
523 1         3 $aggregate->{name} = $name;
524 1         18 $line =~ s/^$name/$value/;
525             }
526              
527             }
528             } else {
529 6         22 foreach my $entry ( split( /[,\s]+/, $value ) ) {
530              
531 7         9 my ($key,$value);
532              
533 7 100       16 if ( $entry =~ /=/ ) {
534 1         5 ($key,$value) = split( /=/, $entry, 2 );
535             } else {
536 6         10 ($key,$value) = ($entry,1);
537             }
538              
539 7         37 $aggregate->{$column}->{$key} = $value;
540              
541             }
542             }
543              
544             }
545              
546 2         8 return $aggregate;
547              
548             }
549              
550             sub _parse_aggr_status_volumes {
551              
552 3     3   1469 my $class = shift;
553              
554 3         75 my %args = validate( @_, {
555             volumes => { type => HASHREF },
556             line => { type => SCALAR },
557             });
558            
559 3         114 my $volumes = $args{volumes};
560 3         5 my $line = $args{line};
561              
562 3         8 $line =~ s/Volumes://g;
563 3         15 $line =~ s/$RE{ws}{crop}//g;
564 3         294 $line =~ s/,//g;
565              
566 3         8 foreach my $volume ( split( /\s+/, $line ) ) {
567 6         15 $volumes->{$volume}++;
568             }
569              
570 3         14 return 1;
571              
572             }
573              
574             sub _parse_aggr_status_plex {
575              
576 1     1   1701 my $class = shift;
577 1         3 my $line = shift;
578              
579 1         6 $line =~ s/$RE{ws}{crop}//g;
580              
581 1 50       221 my ($name,$state) = ( $line =~ m{Plex\s+(\S+): (.*)} ) or
582             croak(
583             "Unable to parse Plex name and state:\n$line\n"
584             );
585              
586             return {
587 3         13 name => $name,
588 1         8 state => { map { $_ => 1 } split( /[,\s]+/, $state ) },
589             };
590              
591             }
592              
593             sub _parse_aggr_status_raidgroup {
594              
595 1     1   1746 my $class = shift;
596 1         2 my $line = shift;
597              
598 1         6 $line =~ s/$RE{ws}{crop}//g;
599              
600 1 50       146 my ($name,$state) = ( $line =~ m{RAID group\s+(\S+): (.*)} ) or
601             croak(
602             "Unable to parse RAIDGroup name and state:\n$line\n"
603             );
604              
605             return {
606 1         6 name => $name,
607 1         5 state => { map { $_ => 1 } split( /[,\s]+/, $state ) },
608             };
609              
610             }
611              
612             1;