File Coverage

blib/lib/NBU/Image.pm
Criterion Covered Total %
statement 25 213 11.7
branch 0 78 0.0
condition 0 6 0.0
subroutine 7 33 21.2
pod 0 26 0.0
total 32 356 8.9


line stmt bran cond sub pod time code
1             #
2             # Copyright (c) 2002 Paul Winkeler. All Rights Reserved.
3             # This program is free software; you may redistribute it and/or modify it under
4             # the same terms as Perl itself.
5             #
6             package NBU::Image;
7              
8 1     1   6 use strict;
  1         2  
  1         35  
9 1     1   5 use Carp;
  1         2  
  1         56  
10              
11 1     1   6 use NBU::Media;
  1         2  
  1         41  
12              
13             my %imageList;
14              
15             my $fileRecursionDepth = 1;
16             my $showEmptyFragments = 0;
17              
18             BEGIN {
19 1     1   5 use Exporter ();
  1         1  
  1         18  
20 1     1   4 use AutoLoader qw(AUTOLOAD);
  1         2  
  1         6  
21 1     1   40 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
  1         2  
  1         149  
22 1     1   3 $VERSION = do { my @r=(q$Revision: 1.39 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
  1         7  
  1         9  
23 1         14 @ISA = qw();
24 1         2 @EXPORT = qw();
25 1         2 @EXPORT_OK = qw();
26 1         2471 %EXPORT_TAGS = qw();
27             }
28              
29             sub new {
30 0     0 0   my $proto = shift;
31 0           my $image;
32              
33 0 0         if (@_) {
34 0           my $backupID = shift;
35 0 0         if (exists($imageList{$backupID})) {
36 0           $image = $imageList{$backupID};
37             }
38             else {
39 0           $image = { };
40 0           bless $image, $proto;
41 0           $imageList{$image->id($backupID)} = $image;
42             }
43             }
44 0           return $image;
45             }
46              
47             sub populate {
48 0     0 0   my $proto = shift;
49              
50 0           $proto->loadImages(NBU->cmd("bpimmedia -l |"));
51             }
52              
53             sub byID {
54 0     0 0   my $proto = shift;
55 0           my $backupID = shift;
56              
57 0 0         if (exists($imageList{$backupID})) {
58 0           return $imageList{$backupID};
59             }
60              
61 0           return undef;
62             }
63              
64             sub list {
65 0     0 0   my $proto = shift;
66              
67 0           return (values %imageList);
68             }
69              
70             sub id {
71 0     0 0   my $self = shift;
72              
73 0 0         if (@_) {
74 0           $self->{BACKUPID} = shift;
75             # The backup ID of an image in part encodes the time the image was
76             # created
77 0           $self->{CTIME} = substr($self->{BACKUPID}, -10);
78             }
79 0           return $self->{BACKUPID};
80             }
81              
82             sub ctime {
83 0     0 0   my $self = shift;
84              
85 0           return $self->{CTIME};
86             }
87              
88             sub client {
89 0     0 0   my $self = shift;
90              
91 0 0         if (@_) {
92 0           my $host = shift;
93 0 0         if (defined($self->{CLIENT})) {
94 0 0         if ((my $oldHost = $self->{CLIENT}) == $host) {
95 0           return $host;
96             }
97             else {
98             # $oldHost->removeImage($self);
99             }
100             }
101 0           $host->addImage($self);
102 0           $self->{CLIENT} = $host;
103             }
104 0           return $self->{CLIENT};
105             }
106              
107             sub class {
108 0     0 0   my $self = shift;
109              
110 0           return $self->policy(@_);
111             }
112              
113             sub policy {
114 0     0 0   my $self = shift;
115              
116 0 0         if (@_) {
117 0           $self->{POLICY} = shift;
118             }
119 0           return $self->{POLICY};
120             }
121              
122             sub schedule {
123 0     0 0   my $self = shift;
124              
125 0 0         if (@_) {
126 0           $self->{SCHEDULE} = shift;
127             }
128 0           return $self->{SCHEDULE};
129             }
130              
131             sub expires {
132 0     0 0   my $self = shift;
133              
134 0 0         if (@_) {
135 0           $self->{EXPIRES} = shift;
136             }
137 0           return $self->{EXPIRES};
138             }
139              
140             sub loadDetail {
141 0     0 0   my $self = shift;
142              
143             #
144             # Layout of this output divined from
145             # http://mailman.eng.auburn.edu/pipermail/veritas-bu/2001-May/003861.html
146 0           my $pipe = NBU->cmd("bpimagelist -l -backupid ".$self->id." |");
147 0           while (<$pipe>) {
148 0 0         next if (/^HIST/);
149 0 0         next if (/^FRAG/);
150 0 0         if (/^IMAGE/) {
151 0           my ($tag, $clientName,
152             $u3, $u4, $u5,
153             $id, $className,
154             $u8, $u9, $u10,
155             $scheduleName,
156             $u12, $u13, $u14,
157             $elapsed,
158             $u16, $u17, $u18,
159             $kbWritten,
160             $fileCount, $copyCount, $fragmentCount,
161             $compressed, $u24,
162             $softwareVersion, $u26, $u27,
163             $primary,
164             $imageType, $TIRInfo, $TIRExpiration,
165             $keywords
166             ) = split;
167 0           $self->{ELAPSED} = $elapsed;
168             }
169             }
170 0           $self->{DETAILED} = 1;
171 0           close($pipe);
172             }
173              
174             sub elapsed {
175 0     0 0   my $self = shift;
176              
177 0 0         $self->loadDetail if (!defined($self->{DETAILED}));
178              
179 0           return $self->{ELAPSED};
180             }
181              
182             sub retention {
183 0     0 0   my $self = shift;
184              
185 0 0         if (@_) {
186 0           $self->{RETENTION} = shift;
187             }
188 0           return $self->{RETENTION};
189             }
190              
191              
192             sub size {
193 0     0 0   my $self = shift;
194 0           my $size;
195              
196 0           for my $f ($self->fragments) {
197 0 0         next unless defined($f);
198 0           $size += $f->size;
199             }
200              
201 0           return $size;
202             }
203              
204             #
205             # Add another fragment to the referenced copy of this image
206             sub insertFragment {
207 0     0 0   my $self = shift;
208 0           my $copy = shift;
209 0           my $fragment = shift;
210            
211 0           my $fragmentListR = $self->{FRAGMENTLISTS};
212 0 0         if (!defined(@$fragmentListR[$copy])) {
213 0           @$fragmentListR[$copy] = [];
214             }
215 0           my $toc = @$fragmentListR[$copy];;
216              
217 0           return $$toc[$fragment->number - 1] = $fragment;
218             }
219              
220             sub fragments {
221 0     0 0   my $self = shift;
222              
223 0           my $fragmentListR = $self->{FRAGMENTLISTS};
224 0 0         if (@_) {
225 0           my $copy = shift;
226 0 0         if (!defined(@$fragmentListR[$copy])) {
227 0           return ();
228             }
229 0           my $toc = @$fragmentListR[$copy];;
230 0           return @$toc;
231             }
232              
233 0           my @toc;
234 0           foreach my $copy (1..$self->copies) {
235 0 0         if (defined(@$fragmentListR[$copy])) {
236 0           my $copytoc = @$fragmentListR[$copy];
237 0           @toc = (@toc, @$copytoc);
238             }
239             }
240 0           return @toc;
241             }
242              
243             sub loadImages {
244 0     0 0   my $proto = shift;
245 0           my $pipe = shift;
246              
247 0           my $image;
248 0           while (<$pipe>) {
249 0 0         if (/^IMAGE/) {
    0          
250 0           my ($tag, $clientName, $clientVersion, $backupID, $className,
251             $classType,
252             $scheduleName,
253             $scheduleType,
254             $retentionLevel, $fileCount, $expires,
255             $compressed, $encrypted
256             ) = split;
257              
258 0           $image = undef;
259 0 0         next if ($expires < time);
260              
261 0           $image = NBU::Image->new($backupID);
262 0           $image->{ENCRYPTED} = $encrypted;
263 0           $image->{COMPRESSED} = $compressed;
264 0           $image->{FILECOUNT} = $fileCount;
265 0           $image->{EXPIRES} = $expires;
266 0           $image->{RETENTION} = NBU::Retention->byLevel($retentionLevel);
267 0           $image->{COPYCOUNT} = 0;
268 0           $image->{FRAGMENTLISTS} = [];
269              
270 0           my $class = $image->class(NBU::Class->new($className, $classType));
271 0           $image->schedule(NBU::Schedule->new($class, $scheduleName, $scheduleType));
272              
273 0           my $host;
274 0           $image->client($host = NBU::Host->new($clientName));
275             }
276             elsif (/^FRAG/) {
277 0 0         next if (!defined($image));
278              
279 0           my ($tag, $copy, $number, $size, $removable,
280             $mediaType, $density, $fileNumber,
281             $rest) = split(/[\s]+/, $_, 9);
282              
283             #
284             # The fragment list contains failed fragments which are left-overs from
285             # failed backups. Normally we skip these, but they can be made visible
286             # when running diagnostics
287             # Such fragments appear to be identified by having a negative fragment number
288 0 0 0       next if (!$showEmptyFragments && (($number < 1)));
289              
290             #
291             # Is this the start of a new copy of this image?
292 0 0         if ($copy > $image->{COPYCOUNT}) {
293 0           $image->{COPYCOUNT} = $copy;
294             }
295              
296 0           my ($mediaID, $volume);
297 0 0 0       if (($removable == 0) && ($mediaType == 0)) {
298             #
299             # Non-removable media (aka disk files) occasionally have spaces in their
300             # names and those will then be surrounded by double quotes...
301             # Additionally we tag these "media" as removable when creating them.
302 0 0         if ($rest =~ s/\"(.*)\"[\s]//) {
303 0           $mediaID = $1;
304             }
305             else {
306 0           $rest =~ s/([\S]+)//;
307 0           $mediaID = $1;
308             }
309 0           $volume = NBU::Media->new($mediaID, undef, 0);
310             }
311             else {
312 0           $rest =~ s/([\S]+)//;
313 0           $mediaID = $1;
314 0           $volume = NBU::Media->new($mediaID, undef, 1);
315 0           $volume->density($density);
316             }
317 0           $rest =~ s/^[\s]*//;
318 0           my ($mmdbHostName,
319             $blockSize, $offset, $allocated, $dwo,
320             $u6, $u7,
321             $expires, $mpx
322             ) = split(/[\s]+/, $rest);
323 0           $volume->mmdbHost(NBU::Host->new($mmdbHostName));
324              
325 0           my $fragment = NBU::Fragment->new($number, $copy, $image, $volume, $offset, $size, $dwo, $fileNumber, $blockSize);
326              
327 0           $volume->insertFragment($fileNumber - 1, $fragment);
328 0           $image->insertFragment($copy, $fragment);
329              
330 0           $image->{REMOVABLE} += $volume->removable;
331              
332 0           $image->volume($volume);
333             }
334             }
335 0           close($pipe);
336             }
337              
338             sub loadFileList {
339 0     0 0   my $self = shift;
340 0           my $func = shift;
341 0           my $depth = shift;
342 0           my @fl;
343            
344 0           my ($s, $m, $h, $dd, $mon, $year, $wday, $yday, $isdst) = localtime($self->ctime);
345 0           my $mm = $mon + 1;
346 0           my $yy = sprintf("%02d", ($year + 1900) % 100);
347              
348 0 0         $depth = $fileRecursionDepth if (!defined($depth));
349 0           my $pipe = NBU->cmd("bpflist -t ANY"
350             ." -option GET_ALL_FILES"
351             ." -client ".$self->client->name
352             ." -backupid ".$self->id
353             ." -d ${mm}/${dd}/${yy}"
354             ." -rl $depth"
355             ." |");
356              
357 0           while (<$pipe>) {
358 0 0         next if (/^FILES/);
359 0           chop;
360             #
361             # Since file names can contain spaces and some bright soul decided to place the file
362             # name in the middle of this line, we need to pick it apart in three pieces:
363             # before, filename, after
364             # More details at https://forums.symantec.com/syment/board/message?board.id=21&thread.id=5475
365 0           my ($i, $compressedFileSize, $pathLength, $u3, $offset,
366             $imageMode, $rawPartitionSize,
367             $largeFileSize, # only set if file over 2GB; units are in GB
368             $physicalDeviceNumber,
369             $rest) = split(/[\s]+/, $_, 10);
370 0 0         if (!($rest =~ /^(.*)[\s]([\S]+)[\s]([\S]+)[\s]([\S]+)[\s]([\S]+)[\s]([\S]+)[\s]([\S]+)[\s]([\S]+)/)) {
371 0           print STDERR "IMAGE filename match failed on $_\n";
372 0           exit;
373             }
374 0           my ($name, $mode, $user, $group, $size,
375             $lastAccessed, $lastModified, $lastInodeModified
376             ) = ($1, $2, $3, $4, $5, $6. $7, $8);
377             # next if ($name =~ /(\/|\\)$/);
378 0 0         if (defined($func)) {
379 0           &$func($name);
380             }
381             else {
382 0           push @fl, $name;
383             }
384             }
385 0           close($pipe);
386              
387 0 0         if (!defined($func)) {
388 0           $self->{FLIST} = \@fl;
389             }
390             }
391              
392             sub fileList {
393 0     0 0   my $self = shift;
394              
395 0 0         if (!$self->{FLIST}) {
396 0           $self->loadFileList(undef, undef);
397             }
398              
399 0 0         if (my $flR = $self->{FLIST}) {
400 0           return @$flR;
401             }
402 0           return undef;
403             }
404              
405             sub copies {
406 0     0 0   my $self = shift;
407              
408 0           return $self->{COPYCOUNT};
409             }
410              
411             #
412             # An image is considered removable if any of its fragments is
413             # is stored on removable media. This attribute is computed during
414             # the loading if the image
415             sub removable {
416 0     0 0   my $self = shift;
417              
418 0           return $self->{REMOVABLE};
419             }
420              
421             sub density {
422 0     0 0   my $self = shift;
423              
424 0 0         return $self->volume->density
425             if ($self->volume);
426 0           return undef;
427             }
428              
429             sub showEmptyFragments {
430 0     0 0   my $proto = shift;
431              
432 0 0         if (@_) {
433 0           $showEmptyFragments = shift;
434             }
435              
436 0           return $showEmptyFragments;
437             }
438              
439             sub fileRecursionDepth {
440 0     0 0   my $proto = shift;
441              
442 0 0         if (@_) {
443 0           $fileRecursionDepth = shift;
444             }
445              
446 0           return $fileRecursionDepth;
447             }
448              
449             sub volume {
450 0     0 0   my $self = shift;
451              
452 0 0         if (@_) {
453 0           my $volume = shift;
454              
455 0           $self->{VOLUME} = $volume;
456             }
457              
458 0           return $self->{VOLUME};
459             }
460              
461             1;
462              
463             __END__