File Coverage

blib/lib/AFS/Command/VOS.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # (c) 2003-2004 Morgan Stanley and Co.
5             # See ..../src/LICENSE for terms of distribution.
6             #
7              
8             package AFS::Command::VOS;
9              
10 3     3   6256 use strict;
  3         7  
  3         210  
11 3     3   16 use English;
  3         6  
  3         23  
12              
13 3     3   4426 use AFS::Command::Base;
  0            
  0            
14             use AFS::Object;
15             use AFS::Object::VLDB;
16             use AFS::Object::VLDBEntry;
17             use AFS::Object::VLDBSite;
18             use AFS::Object::Volume;
19             use AFS::Object::VolumeHeader;
20             use AFS::Object::VolServer;
21             use AFS::Object::FileServer;
22             use AFS::Object::Partition;
23             use AFS::Object::Transaction;
24              
25             our @ISA = qw(AFS::Command::Base);
26             our $VERSION = '1.99';
27              
28             sub examine {
29              
30             my $self = shift;
31             my (%args) = @_;
32              
33             my $result = AFS::Object::Volume->new();
34             my $entry = AFS::Object::VLDBEntry->new( locked => 0 );
35              
36             $self->{operation} = "examine";
37              
38             return unless $self->_parse_arguments(%args);
39              
40             return unless $self->_save_stderr();
41              
42             my $errors = 0;
43              
44             $errors++ unless $self->_exec_cmds();
45              
46             while ( defined($_ = $self->{handle}->getline()) ) {
47              
48             chomp;
49              
50             #
51             # These two lines are part of the verbose output
52             #
53             next if /Fetching VLDB entry/;
54             next if /Getting volume listing/;
55              
56             #
57             # This code parses the volume header information. If we match
58             # this line, then we go after the information we expect to be
59             # right after it. We also test for this first, because we
60             # might very well have several of these chunks of data for RO
61             # volumes.
62             #
63             if ( /^\*{4}/ ) {
64              
65             my $header = AFS::Object::VolumeHeader->new();
66              
67             if ( /Volume (\d+) is busy/ ) {
68             $header->_setAttribute
69             (
70             id => $1,
71             status => 'busy',
72             attached => 1,
73             );
74             } elsif ( /Could not attach volume (\d+)/ ) {
75             $header->_setAttribute
76             (
77             id => $1,
78             status => 'offline',
79             attached => 0,
80             );
81             }
82              
83             $result->_addVolumeHeader($header);
84              
85             next;
86              
87             } elsif ( /^(\S+)\s+(\d+)\s+(RW|RO|BK)\s+(\d+)\s+K/ ) {
88              
89             my $header = AFS::Object::VolumeHeader->new();
90              
91             if ( /^(\S+)\s+(\d+)\s+(RW|RO|BK)\s+(\d+)\s+K\s+([\w-]+)/ ) {
92              
93             $header->_setAttribute
94             (
95             name => $1,
96             id => $2,
97             type => $3,
98             size => $4,
99             );
100             $header->_setAttribute( rwrite => $2 ) if $3 eq 'RW';
101             $header->_setAttribute( ronly => $2 ) if $3 eq 'RO';
102             $header->_setAttribute( backup => $2 ) if $3 eq 'BK';
103              
104             my $status = $5;
105             $status = 'offline' if $status eq 'Off-line';
106             $status = 'online' if $status eq 'On-line';
107             $header->_setAttribute
108             (
109             status => $status,
110             attached => 1,
111             );
112              
113             } elsif ( /^(\S+)\s+(\d+)\s+(RW|RO|BK)\s+(\d+)\s+K\s+used\s+(\d+)\s+files\s+([\w-]+)/ ) {
114              
115             $header->_setAttribute
116             (
117             name => $1,
118             id => $2,
119             type => $3,
120             size => $4,
121             files => $5,
122             );
123             $header->_setAttribute( rwrite => $2 ) if $3 eq 'RW';
124             $header->_setAttribute( ronly => $2 ) if $3 eq 'RO';
125             $header->_setAttribute( backup => $2 ) if $3 eq 'BK';
126              
127             my $status = $6;
128             $status = 'offline' if $status eq 'Off-line';
129             $status = 'online' if $status eq 'On-line';
130             $header->_setAttribute
131             (
132             status => $status,
133             attached => 1,
134             );
135              
136             } else {
137              
138             $self->_Carp("Unable to parse volume header: '$_'");
139              
140             }
141              
142             #
143             # We are interested in the next 6 lines as they are also
144             # from the same volume headers as the one we just matched.
145             # Suck data until we get to a blank line.
146             #
147             while ( defined($_ = $self->{handle}->getline()) ) {
148              
149             chomp;
150              
151             last if /^\s*$/; # Stop when we hit the blank line
152              
153             if ( m:^\s+(\S+)\s+(/vicep\w+)\s*$: ) {
154             $header->_setAttribute
155             (
156             server => $1,
157             partition => $2,
158             );
159             next;
160             }
161              
162             #
163             # Next we get ALL the volume IDs we can off this next
164             # line.
165             #
166             # Q: Do we want to check that the id already found
167             # matches one of these?? Not yet...
168             #
169             if ( /^\s+RWrite\s+(\d+)\s+ROnly\s+(\d+)\s+Backup\s+(\d+)/ ) {
170              
171             $header->_setAttribute
172             (
173             rwrite => $1,
174             ronly => $2,
175             backup => $3,
176             );
177              
178             if ( /RClone\s+(\d+)/ ) {
179             $header->_setAttribute( rclone => $1 );
180             }
181             next;
182              
183             }
184              
185             if ( /^\s+MaxQuota\s+(\d+)/ ) {
186             $header->_setAttribute( maxquota => $1 );
187             next;
188             }
189              
190             if ( /^\s+Creation\s+(.*)\s*$/ ) {
191             $header->_setAttribute( creation => $1 );
192             next;
193             }
194              
195             if ( /^\s+Copy\s+(.*)\s*$/ ) {
196             $header->_setAttribute( copyTime => $1 );
197             next;
198             }
199              
200             if ( /^\s+Backup\s+(.*)\s*$/ ) {
201             $header->_setAttribute( backupTime => $1 );
202             next;
203             }
204              
205             if ( /^\s+Last Access\s+(.*)\s*$/ ) {
206             $header->_setAttribute( access => $1 );
207             next;
208             }
209              
210             if ( /^\s+Last Update\s+(.*)\s*$/ ) {
211             $header->_setAttribute( update => $1 );
212             next;
213             }
214              
215             if ( /^\s+(\d+) accesses/ ) {
216             $header->_setAttribute( accesses => $1 );
217             next;
218             }
219              
220             #
221             # If we get this far, then we have an unrecognized
222             # line of vos examine output. Complain.
223             #
224             $self->_Carp("Unrecognized output format:\n" . $_);
225              
226             }
227              
228             #
229             # Are we looking for extended data??
230             #
231             if ( $args{extended} ) {
232              
233             my $raw = AFS::Object->new();
234             my $author = AFS::Object->new();
235              
236             my $boundary = 0;
237              
238             while ( defined($_ = $self->{handle}->getline()) ) {
239              
240             chomp;
241              
242             $boundary++ if /^\s+\|-+\|\s*$/;
243              
244             last if /^\s*$/ && $boundary == 4;
245              
246             next unless /\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|/;
247              
248             my @column = ( $1, $2, $3, $4 );
249              
250             my $class = "";
251             my $int = "";
252              
253             $class = 'reads' if /^Reads/;
254             $class = 'writes' if /^Writes/;
255              
256             if ( $class ) {
257              
258             my $same = AFS::Object->new
259             (
260             total => $column[0],
261             auth => $column[1],
262             );
263              
264             my $diff = AFS::Object->new
265             (
266             total => $column[2],
267             auth => $column[3],
268             );
269              
270             my $stats = AFS::Object->new
271             (
272             same => $same,
273             diff => $diff,
274             );
275              
276             $raw->_setAttribute( $class => $stats );
277              
278             }
279              
280             $int = '0sec' if /^0-60 sec/;
281             $int = '1min' if /^1-10 min/;
282             $int = '10min' if /^10min-1hr/;
283             $int = '1hr' if /^1hr-1day/;
284             $int = '1day' if /^1day-1wk/;
285             $int = '1wk' if /^> 1wk/;
286              
287             if ( $int ) {
288              
289             my $file = AFS::Object->new
290             (
291             same => $column[0],
292             diff => $column[1],
293             );
294              
295             my $dir = AFS::Object->new
296             (
297             same => $column[2],
298             diff => $column[3],
299             );
300              
301             my $stats = AFS::Object->new
302             (
303             file => $file,
304             dir => $dir,
305             );
306              
307             $author->_setAttribute( $int => $stats );
308              
309             }
310              
311             }
312              
313             $header->_setAttribute
314             (
315             raw => $raw,
316             author => $author,
317             );
318              
319             }
320              
321             $result->_addVolumeHeader($header);
322              
323             next;
324              
325             }
326              
327             #
328             # The rest of the information we get will be from the
329             # VLDB. This will start with the volume ids, which we DO want
330             # to check against those found above, since they are from a
331             # different source, and a conflict is cause for concern.
332             #
333             if ( /^\s+RWrite:\s+(\d+)/ ) {
334              
335             if ( /RWrite:\s+(\d+)/ ) { $entry->_setAttribute( rwrite => $1 ); }
336             if ( /ROnly:\s+(\d+)/ ) { $entry->_setAttribute( ronly => $1 ); }
337             if ( /Backup:\s+(\d+)/ ) { $entry->_setAttribute( backup => $1 ); }
338             if ( /RClone:\s+(\d+)/ ) { $entry->_setAttribute( rclone => $1 ); }
339              
340             next;
341              
342             } # if ( /^\s+RWrite:....
343              
344             #
345             # Next we are looking for the number of sites, and then we'll
346             # suck that data in as well.
347             #
348             # NOTE: Because there is more interesting data after the
349             # locations, we fall through to the next test once we are done
350             # parsing them.
351             #
352             if ( /^\s+number of sites ->\s+(\d+)/ ) {
353              
354             while ( defined($_ = $self->{handle}->getline()) ) {
355              
356             chomp;
357              
358             last unless m:^\s+server\s+(\S+)\s+partition\s+(/vicep\w+)\s+([A-Z]{2})\s+Site\s*(--\s+)?(.*)?:;
359              
360             my $site = AFS::Object::VLDBSite->new
361             (
362             server => $1,
363             partition => $2,
364             type => $3,
365             status => $5,
366             );
367              
368             $entry->_addVLDBSite($site);
369              
370             }
371              
372             }
373              
374             #
375             # Last possibility (that we know of) -- volume might be
376             # locked.
377             #
378             if ( /LOCKED/ ) {
379             $entry->_setAttribute( locked => 1 );
380             next;
381             }
382              
383             #
384             # Actually, this is the last possibility... The volume name
385             # leading the VLDB entry stanza.
386             #
387             if ( /^(\S+)/ ) {
388             $entry->_setAttribute( name => $1 );
389             }
390              
391             }
392              
393             $result->_addVLDBEntry($entry);
394              
395             $errors++ unless $self->_reap_cmds();
396              
397             $errors++ unless $self->_restore_stderr();
398              
399             return if $errors;
400             return $result;
401              
402             }
403              
404             sub listaddrs {
405              
406             my $self = shift;
407             my (%args) = @_;
408              
409             my @result = ();
410              
411             $self->{operation} = "listaddrs";
412              
413             return unless $self->_parse_arguments(%args);
414              
415             return unless $self->_save_stderr();
416              
417             my $errors = 0;
418              
419             $errors++ unless $self->_exec_cmds();
420              
421             if ( $args{printuuid} ) {
422              
423             while ( defined($_ = $self->{handle}->getline()) ) {
424              
425             chomp;
426              
427             if ( /^UUID:\s+(\S+)/ ) {
428              
429             my $fileserver = AFS::Object::FileServer->new( uuid => $1 );
430              
431             my @addresses = ();
432             my $hostname = "";
433              
434             while ( defined($_ = $self->{handle}->getline()) ) {
435             s/^\s*//g;
436             s/\s*$//g;
437             last if /^\s*$/;
438             chomp;
439             if ( /^\d+\.\d+\.\d+\.\d+$/ ) {
440             push(@addresses,$_);
441             } else {
442             $hostname = $_;
443             }
444             }
445              
446             $fileserver->_setAttribute( addresses => \@addresses ) if @addresses;
447             $fileserver->_setAttribute( hostname => $hostname ) if $hostname;
448              
449             push(@result,$fileserver);
450              
451             }
452              
453             }
454              
455             } elsif ( $args{uuid} ) {
456              
457             my @addresses = ();
458             my $hostname = "";
459              
460             while ( defined($_ = $self->{handle}->getline()) ) {
461             chomp;
462             s/^\s*//g;
463             s/\s*$//g;
464             if ( /^\d+\.\d+\.\d+\.\d+$/ ) {
465             push(@addresses,$_);
466             } else {
467             $hostname = $_;
468             }
469             }
470              
471             if ( $hostname || @addresses ) {
472             my $fileserver = AFS::Object::FileServer->new();
473             $fileserver->_setAttribute( addresses => \@addresses ) if @addresses;
474             $fileserver->_setAttribute( hostname => $hostname ) if $hostname;
475             push(@result,$fileserver);
476             }
477              
478             } else {
479              
480             while ( defined($_ = $self->{handle}->getline()) ) {
481             chomp;
482             s/^\s*//g;
483             s/\s*$//g;
484             if ( /^\d+\.\d+\.\d+\.\d+$/ ) {
485             push(@result,AFS::Object::FileServer->new( addresses => [$_] ));
486             } else {
487             push(@result,AFS::Object::FileServer->new( hostname => $_ ));
488             }
489             }
490              
491             }
492              
493             $errors++ unless $self->_reap_cmds();
494             $errors++ unless $self->_restore_stderr();
495              
496             return if $errors;
497             return @result;
498              
499             }
500              
501             sub listpart {
502              
503             my $self = shift;
504             my (%args) = @_;
505              
506             my $result = AFS::Object::FileServer->new();
507              
508             $self->{operation} = "listpart";
509              
510             return unless $self->_parse_arguments(%args);
511              
512             return unless $self->_save_stderr();
513              
514             my $errors = 0;
515              
516             $errors++ unless $self->_exec_cmds();
517              
518             while ( defined($_ = $self->{handle}->getline()) ) {
519              
520             chomp;
521              
522             next unless m:/vice:;
523              
524             s/^\s+//g;
525             s/\s+$//g;
526              
527             foreach my $partname ( split ) {
528             my $partition = AFS::Object::Partition->new( partition => $partname );
529             $result->_addPartition($partition);
530             }
531              
532             }
533              
534             $errors++ unless $self->_reap_cmds();
535             $errors++ unless $self->_restore_stderr();
536              
537             return if $errors;
538             return $result;
539              
540             }
541              
542             sub listvldb {
543              
544             my $self = shift;
545             my (%args) = @_;
546              
547             $self->{operation} = "listvldb";
548              
549             my $locked = 0;
550              
551             my $result = AFS::Object::VLDB->new();
552              
553             return unless $self->_parse_arguments(%args);
554              
555             return unless $self->_save_stderr();
556              
557             my $errors = 0;
558              
559             $errors++ unless $self->_exec_cmds();
560              
561             while ( defined($_ = $self->{handle}->getline()) ) {
562              
563             chomp;
564              
565             next if /^\s*$/; # If it starts with a blank line, then
566             # its not a volume name.
567             #
568             # Skip the introductory lines of the form:
569             # "VLDB entries for all servers"
570             # "VLDB entries for server ny91af01"
571             # "VLDB entries for server ny91af01 partition /vicepa"
572             #
573             next if /^VLDB entries for /;
574              
575             s/\s+$//g; # Might be trailing whitespace...
576              
577             #
578             # We either get the total number of volumes, or we assume the
579             # line is a volume name.
580             #
581             if ( /Total entries:\s+(\d+)/ ) {
582             $result->_setAttribute( total => $1 );
583             next;
584             }
585              
586             my $name = $_;
587              
588             my $entry = AFS::Object::VLDBEntry->new( name => $name );
589              
590             while ( defined($_ = $self->{handle}->getline()) ) {
591              
592             chomp;
593              
594             last if /^\s*$/; # Volume info ends with a blank line
595              
596             #
597             # Code to parse this output lives in examine.pl. This
598             # will need to be made generic and used here to parse and
599             # return the full vldb entry.
600             #
601              
602             if ( /RWrite:\s+(\d+)/ ) { $entry->_setAttribute( rwrite => $1 ); }
603             if ( /ROnly:\s+(\d+)/ ) { $entry->_setAttribute( ronly => $1 ); }
604             if ( /Backup:\s+(\d+)/ ) { $entry->_setAttribute( backup => $1 ); }
605             if ( /RClone:\s+(\d+)/ ) { $entry->_setAttribute( rclone => $1 ); }
606              
607             if ( /^\s+number of sites ->\s+(\d+)/ ) {
608              
609             my $sites = $1;
610              
611             while ( defined($_ = $self->{handle}->getline()) ) {
612              
613             chomp;
614              
615             next unless m:^\s+server\s+(\S+)\s+partition\s+(/vicep\w+)\s+([A-Z]{2})\s+Site\s*(--\s+)?(.*)?:;
616              
617             $sites--;
618              
619             my $site = AFS::Object::VLDBSite->new
620             (
621             server => $1,
622             partition => $2,
623             type => $3,
624             status => $5,
625             );
626              
627             $entry->_addVLDBSite( $site );
628              
629             last if $sites == 0;
630              
631             }
632              
633             }
634              
635             #
636             # Last possibility (that we know of) -- volume might be
637             # locked.
638             #
639             if ( /LOCKED/ ) {
640             $entry->_setAttribute( locked => 1 );
641             $locked++;
642             }
643              
644             }
645              
646             $result->_addVLDBEntry( $entry );
647              
648             }
649              
650             $result->_setAttribute( locked => $locked );
651              
652             $errors++ unless $self->_reap_cmds();
653              
654             $errors++ unless $self->_restore_stderr();
655              
656             return if $errors;
657             return $result;
658              
659             }
660              
661              
662             sub listvol {
663              
664             my $self = shift;
665             my (%args) = @_;
666              
667             my $result = AFS::Object::VolServer->new();
668              
669             $self->{operation} = "listvol";
670              
671             return unless $self->_parse_arguments(%args);
672              
673             return unless $self->_save_stderr();
674              
675             my $errors = 0;
676              
677             $errors++ unless $self->_exec_cmds();
678              
679             if ( delete $args{extended} ) {
680             $self->_Carp("vos listvol: -extended is not supported by this version of the API");
681             }
682              
683             while ( defined($_ = $self->{handle}->getline()) ) {
684              
685             chomp;
686              
687             next if /^\s*$/; # Blank lines are not interesting
688              
689             next unless /^Total number of volumes on server \S+ partition (\/vice[\w]+): (\d+)/;
690              
691             my $partition = AFS::Object::Partition->new
692             (
693             partition => $1,
694             total => $2,
695             );
696              
697             while ( defined($_ = $self->{handle}->getline()) ) {
698              
699             chomp;
700              
701             last if /^\s*$/ && $args{fast};
702              
703             next if /^\s*$/;
704              
705             s/\s+$//;
706              
707             if ( /^Total volumes onLine (\d+) ; Total volumes offLine (\d+) ; Total busy (\d+)/ ) {
708             $partition->_setAttribute
709             (
710             online => $1,
711             offline => $2,
712             busy => $3,
713             );
714             last; # Done with this partition
715             }
716              
717             if ( /Volume (\d+) is busy/ ) {
718             my $volume = AFS::Object::VolumeHeader->new
719             (
720             id => $1,
721             status => 'busy',
722             attached => 1,
723             );
724             $partition->_addVolumeHeader($volume);
725             next;
726             } elsif ( /Could not attach volume (\d+)/ ) {
727             my $volume = AFS::Object::VolumeHeader->new
728             (
729             id => $1,
730             status => 'offline',
731             attached => 0,
732             );
733             $partition->_addVolumeHeader($volume);
734             next;
735             }
736              
737             #
738             # We have to handle multiple formats here. For
739             # now, just parse the "fast" and normal output.
740             # Extended is not yet supported.
741             #
742              
743             my (@array) = split;
744             my ($name,$id,$type,$size,$status) = ();
745              
746             my $volume = AFS::Object::VolumeHeader->new();
747              
748             if ( @array == 6 ) {
749             ($name,$id,$type,$size,$status) = @array[0..3,5];
750             $status = 'offline' if $status eq 'Off-line';
751             $status = 'online' if $status eq 'On-line';
752             $volume->_setAttribute
753             (
754             id => $id,
755             name => $name,
756             type => $type,
757             size => $size,
758             status => $status,
759             attached => 1,
760             );
761             } elsif ( @array == 1 ) {
762             $volume->_setAttribute
763             (
764             id => $_,
765             status => 'online',
766             attached => 1,
767             );
768             } else {
769             $self->_Carp("Unable to parse header summary line:\n" . $_);
770             $errors++;
771             next;
772             }
773              
774             #
775             # If the output is long, then we have some more
776             # interesting information to parse. See vos/examine.pl
777             # for notes. This code was stolen from there...
778             #
779              
780             if ( $args{long} || $args{extended} ) {
781              
782             while ( defined($_ = $self->{handle}->getline()) ) {
783              
784             last if /^\s*$/;
785              
786             if ( /^\s+RWrite\s+(\d+)\s+ROnly\s+(\d+)\s+Backup\s+(\d+)/ ) {
787             $volume->_setAttribute
788             (
789             rwrite => $1,
790             ronly => $2,
791             backup => $3,
792             );
793             if ( /RClone\s+(\d+)/ ) {
794             $volume->_setAttribute( rclone => $1 );
795             }
796             next;
797             }
798              
799             if ( /^\s+MaxQuota\s+(\d+)/ ) {
800             $volume->_setAttribute( maxquota => $1 );
801             next;
802             }
803              
804             if ( /^\s+Creation\s+(.*)\s*$/ ) {
805             $volume->_setAttribute( creation => $1 );
806             next;
807             }
808              
809             if ( /^\s+Copy\s+(.*)\s*$/ ) {
810             $volume->_setAttribute( copyTime => $1 );
811             next;
812             }
813              
814             if ( /^\s+Backup\s+(.*)\s*$/ ) {
815             $volume->_setAttribute( backupTime => $1 );
816             next;
817             }
818              
819             if ( /^\s+Last Access\s+(.*)\s*$/ ) {
820             $volume->_setAttribute( access => $1 );
821             next;
822             }
823              
824             if ( /^\s+Last Update\s+(.*)\s*$/ ) {
825             $volume->_setAttribute( update => $1 );
826             next;
827             }
828              
829             if ( /^\s+(\d+) accesses/ ) {
830             $volume->_setAttribute( accesses => $1 );
831             next;
832             }
833             } # while(defined($_ = $self->{handle}->getline())) {
834              
835             }
836              
837             $partition->_addVolumeHeader($volume);
838              
839             }
840              
841             $result->_addPartition($partition);
842              
843             }
844              
845             $errors++ unless $self->_reap_cmds();
846              
847             $errors++ unless $self->_restore_stderr();
848              
849             return if $errors;
850             return $result;
851              
852             }
853              
854             sub partinfo {
855              
856             my $self = shift;
857             my (%args) = @_;
858              
859             my $result = AFS::Object::FileServer->new();
860              
861             $self->{operation} = "partinfo";
862              
863             return unless $self->_parse_arguments(%args);
864              
865             return unless $self->_save_stderr();
866              
867             my $errors = 0;
868              
869             $errors++ unless $self->_exec_cmds();
870              
871             while ( defined($_ = $self->{handle}->getline()) ) {
872              
873             next unless m|partition (/vice\w+): (-?\d+)\D+(\d+)$|;
874              
875             my $partition = AFS::Object::Partition->new
876             (
877             partition => $1,
878             available => $2,
879             total => $3,
880             );
881              
882             $result->_addPartition($partition);
883              
884             }
885              
886             $errors++ unless $self->_reap_cmds();
887              
888             $errors++ unless $self->_restore_stderr();
889              
890             return if $errors;
891             return $result;
892              
893             }
894              
895             sub status {
896              
897             my $self = shift;
898             my (%args) = @_;
899              
900             my $result = AFS::Object::VolServer->new();
901              
902             $self->{operation} = "status";
903              
904             return unless $self->_parse_arguments(%args);
905              
906             return unless $self->_save_stderr();
907              
908             my $errors = 0;
909              
910             $errors++ unless $self->_exec_cmds();
911              
912             my $transaction = undef;
913              
914             while ( defined($_ = $self->{handle}->getline()) ) {
915              
916             chomp;
917              
918             if ( /No active transactions/ ) {
919             $result->_setAttribute( transactions => 0 );
920             last;
921             }
922              
923             if ( /Total transactions: (\d+)/ ) {
924             $result->_setAttribute( transactions => $1 );
925             next;
926             }
927              
928             if ( /^-+\s*$/ ) {
929              
930             if ( $transaction ) {
931             $result->_addTransaction($transaction);
932             $transaction = undef;
933             } else {
934             $transaction = AFS::Object::Transaction->new();
935             }
936              
937             }
938              
939             next unless $transaction;
940              
941             if ( /transaction:\s+(\d+)/ ) {
942             $transaction->_setAttribute( transaction => $1 );
943             }
944              
945             if ( /created:\s+(.*)$/ ) {
946             $transaction->_setAttribute( created => $1 );
947             }
948              
949             if ( /attachFlags:\s+(.*)$/ ) {
950             $transaction->_setAttribute( attachFlags => $1 );
951             }
952              
953             if ( /volume:\s+(\d+)/ ) {
954             $transaction->_setAttribute( volume => $1 );
955             }
956              
957             if ( /partition:\s+(\S+)/ ) {
958             $transaction->_setAttribute( partition => $1 );
959             }
960              
961             if ( /procedure:\s+(\S+)/ ) {
962             $transaction->_setAttribute( procedure => $1 );
963             }
964              
965             if ( /packetRead:\s+(\d+)/ ) {
966             $transaction->_setAttribute( packetRead => $1 );
967             }
968              
969             if ( /lastReceiveTime:\s+(\d+)/ ) {
970             $transaction->_setAttribute( lastReceiveTime => $1 );
971             }
972              
973             if ( /packetSend:\s+(\d+)/ ) {
974             $transaction->_setAttribute( packetSend => $1 );
975             }
976              
977             if ( /lastSendTime:\s+(\d+)/ ) {
978             $transaction->_setAttribute( lastSendTime => $1 );
979             }
980              
981             }
982              
983             $errors++ unless $self->_reap_cmds();
984              
985             $errors++ unless $self->_restore_stderr();
986              
987             return if $errors;
988             return $result;
989              
990             }
991              
992             sub dump {
993              
994             my $self = shift;
995             my (%args) = @_;
996              
997             $self->{operation} = 'dump';
998              
999             my $file = delete $args{file} || do {
1000             $self->_Carp("Missing required argument: 'file'");
1001             return;
1002             };
1003              
1004             my $gzip_default = 6;
1005             my $bzip2_default = 6;
1006              
1007             my $nocompress = delete $args{nocompress} || undef;
1008             my $gzip = delete $args{gzip} || undef;
1009             my $bzip2 = delete $args{bzip2} || undef;
1010             my $filterout = delete $args{filterout} || undef;
1011              
1012             if ( $gzip && $bzip2 && $nocompress ) {
1013             $self->_Carp("Invalid argument combination: only one of 'gzip' or 'bzip2' or 'nocompress' may be specified");
1014             return;
1015             }
1016              
1017             if ( $file eq 'stdin' ) {
1018             $self->_Carp("Invalid argument 'stdin': you can't write output to stdin");
1019             return;
1020             }
1021              
1022             if ( $file ne 'stdout' ) {
1023              
1024             if ( $file =~ /\.gz$/ && not defined $gzip and not defined $nocompress ) {
1025             $gzip = $gzip_default;
1026             } elsif ( $file =~ /\.bz2$/ && not defined $bzip2 and not defined $nocompress ) {
1027             $bzip2 = $bzip2_default;
1028             }
1029              
1030             if ( $gzip && $file !~ /\.gz$/ ) {
1031             $file .= ".gz";
1032             } elsif ( $bzip2 && $file !~ /\.bz2/ ) {
1033             $file .= ".bz2";
1034             }
1035              
1036             unless ( $gzip || $bzip2 || $filterout ) {
1037             $args{file} = $file;
1038             }
1039              
1040             }
1041              
1042             return unless $self->_parse_arguments(%args);
1043              
1044             if ( $filterout ) {
1045              
1046             unless ( ref $filterout eq 'ARRAY' ) {
1047             $self->_Carp("Invalid argument 'filterout': must be an ARRAY reference");
1048             return;
1049             }
1050              
1051             if ( ref($filterout->[0]) eq 'ARRAY' ) {
1052             foreach my $filter ( @$filterout ) {
1053             unless ( ref $filter eq 'ARRAY' ) {
1054             $self->_Carp("Invalid argument 'filterout': must be an ARRAY of ARRAY references, \n" .
1055             "OR an ARRAY of strings. See the documentation for details");
1056             return;
1057             }
1058             push( @{$self->{cmds}}, $filter );
1059             }
1060             } else {
1061             push( @{$self->{cmds}}, $filterout );
1062             }
1063              
1064             };
1065              
1066             if ( $gzip ) {
1067             push( @{$self->{cmds}}, [ 'gzip', "-$gzip", '-c' ] );
1068             } elsif ( $bzip2 ) {
1069             push( @{$self->{cmds}}, [ 'bzip2', "-$bzip2", '-c' ] );
1070             }
1071              
1072             return unless $self->_save_stderr();
1073              
1074             my $errors = 0;
1075              
1076             $errors++ unless $self->_exec_cmds
1077             (
1078             stdout => ( $args{file} ? "/dev/null" : $file ),
1079             );
1080              
1081             $errors++ unless $self->_reap_cmds();
1082              
1083             $errors++ unless $self->_restore_stderr();
1084              
1085             return if $errors;
1086             return 1;
1087              
1088             }
1089              
1090             sub restore {
1091              
1092             my $self = shift;
1093             my (%args) = @_;
1094              
1095             $self->{operation} = "restore";
1096              
1097             my $file = delete $args{file} || do {
1098             $self->_Carp("Missing required argument: 'file'");
1099             return;
1100             };
1101              
1102             my $nocompress = delete $args{nocompress} || undef;
1103             my $gunzip = delete $args{gunzip} || undef;
1104             my $bunzip2 = delete $args{bunzip2} || undef;
1105             my $filterin = delete $args{filterin} || undef;;
1106              
1107             if ( $gunzip && $bunzip2 && $nocompress ) {
1108             $self->_Carp("Invalid argument combination: only one of 'gunzip' or 'bunzip2' or 'nocompress' may be specified");
1109             return;
1110             }
1111              
1112             if ( $file eq 'stdout' ) {
1113             $self->_Carp("Invalid argument 'stdout': you can't read input from stdout");
1114             return;
1115             }
1116              
1117             if ( $file ne 'stdin' ) {
1118              
1119             if ( $file =~ /\.gz$/ && not defined $gunzip and not defined $nocompress ) {
1120             $gunzip = 1;
1121             } elsif ( $file =~ /\.bz2$/ && not defined $bunzip2 and not defined $nocompress ) {
1122             $bunzip2 = 1;
1123             }
1124              
1125             unless ( $gunzip || $bunzip2 || $filterin ) {
1126             $args{file} = $file;
1127             }
1128              
1129             }
1130              
1131             return unless $self->_parse_arguments(%args);
1132              
1133             if ( $filterin ) {
1134              
1135             unless ( ref $filterin eq 'ARRAY' ) {
1136             $self->_Carp("Invalid argument 'filterin': must be an ARRAY reference");
1137             return;
1138             }
1139              
1140             if ( ref($filterin->[0]) eq 'ARRAY' ) {
1141             foreach my $filter ( @$filterin ) {
1142             unless ( ref $filter eq 'ARRAY' ) {
1143             $self->_Carp("Invalid argument 'filterin': must be an ARRAY of ARRAY references, \n" .
1144             "OR an ARRAY of strings. See the documentation for details");
1145             return;
1146             }
1147             unshift( @{$self->{cmds}}, $filter );
1148             }
1149             } else {
1150             unshift( @{$self->{cmds}}, $filterin );
1151             }
1152              
1153             };
1154              
1155             if ( $gunzip ) {
1156             unshift( @{$self->{cmds}}, [ 'gunzip', '-c' ] );
1157             } elsif ( $bunzip2 ) {
1158             unshift( @{$self->{cmds}}, [ 'bunzip2', '-c' ] );
1159             }
1160              
1161             my $errors = 0;
1162              
1163             $errors++ unless $self->_exec_cmds
1164             (
1165             stderr => 'stdout',
1166             stdin => ( $args{file} ? "/dev/null" : $file ),
1167             );
1168              
1169             $errors++ unless $self->_parse_output();
1170             $errors++ unless $self->_reap_cmds();
1171              
1172             return if $errors;
1173             return 1;
1174              
1175             }
1176              
1177             1;
1178