File Coverage

blib/lib/NBU/Media.pm
Criterion Covered Total %
statement 31 501 6.1
branch 0 230 0.0
condition 0 45 0.0
subroutine 9 78 11.5
pod 0 69 0.0
total 40 923 4.3


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::Media;
7              
8 1     1   5 use strict;
  1         1  
  1         29  
9 1     1   5 use Carp;
  1         1  
  1         63  
10              
11 1     1   835 use Date::Parse;
  1         8336  
  1         154  
12              
13 1     1   684 use NBU::Robot;
  1         2  
  1         34  
14              
15             BEGIN {
16 1     1   6 use Exporter ();
  1         2  
  1         17  
17 1     1   5 use AutoLoader qw(AUTOLOAD);
  1         2  
  1         7  
18 1     1   35 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
  1         1  
  1         78  
19 1     1   4 use vars qw(%densities %mediaTypes);
  1         2  
  1         102  
20 1     1   2 $VERSION = do { my @r=(q$Revision: 1.45 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
  1         5  
  1         7  
21 1         12 @ISA = qw();
22 1         2 @EXPORT = qw(%densities);
23 1         3 @EXPORT_OK = qw();
24 1         6494 %EXPORT_TAGS = qw();
25             }
26              
27             %densities = (
28             13 => "dlt",
29             16 => "8mm",
30             12 => "4mm",
31             6 => "hcart",
32             19 => "dtf",
33             9 => "odiskwm",
34             10 => "odiskwo",
35             0 => "qscsi",
36             15 => "dlt2",
37             14 => "hcart2",
38             20 => "hcart3",
39             21 => "dlt3",
40             );
41              
42             my %mediaCodes = (
43             'DLT' => 11,
44             'DLT_CLN'=> 12,
45             'DLT2' => 16,
46             'DLT2_CLN' => 17,
47             'DLT3' => 26,
48             'DLT3_CLN' => 27,
49             'HCART' => 6,
50             'HC_CLN' => 13,
51             'HCART2' => 14,
52             'HC2_CLN' => 15,
53             'HCART3' => 24,
54             'HC3_CLN' => 25,
55             '4MM' => 9,
56             '4MM_CLN' => 10,
57             '8MM' => 4,
58             '8MM_CLN' => 5,
59             # '8MM2' =>
60             # '8MM2_CLN' =>
61             # 'D2' =>
62             # 'D2_CLN' =>
63             'DTF' => 22,
64             'DTF_CLN' => 23,
65             'DEFAULT' => 0,
66             );
67              
68             %mediaTypes = (
69             0 => "DEFAULT",
70             11 => "DLT cartridge tape",
71             12 => "DLT cleaning tape",
72             16 => "DLT cartridge tape 2",
73             17 => "DLT cleaning tape 2",
74             26 => "DLT cartridge tape 3",
75             27 => "DLT cleaning tape 3",
76             6 => "1/2\" cartridge tape",
77             13 => "1/2\" cleaning tape",
78             14 => "1/2\" cartridge tape 2",
79             15 => "1/2\" cleaning tape 2",
80             24 => "1/2\" cartridge tape 3",
81             25 => "1/2\" cleaning tape 3",
82             4 => "8MM cartridge tape",
83             5 => "8MM cleaning tape",
84             9 => "4MM cartridge tape",
85             10 => "4MM cleaning tape",
86             22 => "DTF cartridge tape",
87             23 => "DTF cleaning tape",
88             1 => "Rewritable optical disk",
89             2 => "WORM optical disk",
90             8 => "QIC - 1/4\" cartridge tape",
91             );
92              
93             my %mediaList;
94             my %barcodeList;
95              
96             sub new {
97 0     0 0   my $proto = shift;
98 0           my $media = {};
99              
100 0           bless $media, $proto;
101              
102 0 0         if (@_) {
103 0           my $mediaID = shift;
104 0           my $voldbHost = shift;
105              
106 0 0         if (exists($mediaList{$mediaID})) {
107 0           $media = $mediaList{$mediaID};
108             }
109             else {
110 0           $media->{VOLDBHOST} = $voldbHost;
111 0           $media->{RVSN} = $mediaID;
112 0           $mediaList{$media->{RVSN}} = $media;
113             }
114 0 0         if (@_) {
115 0           my $removable = shift;
116 0           $media->{REMOVABLE} = $removable;
117             }
118             else {
119 0           $media->{REMOVABLE} = 1;
120             }
121             }
122 0           return $media;
123             }
124              
125             my $filled;
126             sub populate {
127 0     0 0   my $proto = shift;
128 0           my $volume;
129 0           my $updateRobot = shift;
130              
131 0           my $pipe;
132              
133 0           $filled = 0;
134              
135             #
136             # Have to force the pool information to load or we dead-lock. It appears
137             # the VM database deamon is single threaded and won't answer a pool query
138             # until the volume listing is completed...
139 0           NBU::Pool->populate;
140              
141             #
142             # The rule is to populate with information from all the volume databases
143             # maintained by the master and its active media servers (allowing for the
144             # master in fact not to be a media server).
145 0           my %voldbHosts;
146 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
147 0           $voldbHosts{$master->name} = $master;
148 0           foreach my $ms (NBU::StorageUnit->mediaServers($master)) {
149 0 0         if (defined(my $EMMserver = $ms->EMMserver)) {
150 0           $voldbHosts{$EMMserver->name} = $EMMserver;
151             }
152             else {
153 0           $voldbHosts{$ms->name} = $ms;
154             }
155             }
156              
157 0           foreach my $voldbHost (values %voldbHosts) {
158 0           $pipe = NBU->cmd("vmquery -a -w -h ".$voldbHost->name." |");
159 0           $_ = <$pipe>; $_ = <$pipe>; $_ = <$pipe>;
  0            
  0            
160 0           while (<$pipe>) {
161 0           my ($id,
162             $opticalPartner,
163             $mediaCode,
164             $barcode, $barcodePartner,
165             $robotHostName, $robotType, $robotNumber, $slotNumber,
166             $side,
167             $volumeGroup,
168             $volumePool, $volumePoolNumber, $previousVolumePool,
169             $mountCount, $maxMounts, $cleaningCount,
170             $creationDate, $creationTime,
171             $assignDate, $assignTime,
172             $firstMountDate, $firstMountTime,
173             $lastMountDate, $lastMountTime,
174             $expirationDate, $expirationTime,
175             $status,
176             $offsiteLocation,
177             $offsiteSentDate, $offsiteSentTime,
178             $offsiteReturnDate, $offsiteReturnTime,
179             $offsiteSlot,
180             $offsiteSessionID,
181             $version,
182             $description,
183             )
184             = split(/[\s]+/, $_, 37);
185              
186             #
187             # "Normal" installations will not use the same serial number in more than
188             # one volume database. Thus our test here more or less expects not to
189             # find this id:
190 0           $volume = NBU::Media->byID($id, $voldbHost);
191 0 0         if (defined($volume)) {
192 0 0         if (NBU->debug) {
193 0           print STDERR "Volume $id in voldb on ".$voldbHost->name." conflicts with existing volume\n";
194 0           print STDERR " Host: ".$volume->voldbHost."\n";
195             }
196 0           next;
197             }
198             else {
199 0           $volume = NBU::Media->new($id, $voldbHost);
200              
201 0           $filled += 1;
202             }
203              
204 0           $volume->barcode($barcode);
205              
206 0 0         if (!exists($mediaCodes{$mediaCode})) {
207             }
208 0           $volume->{MEDIATYPE} = $mediaCodes{$mediaCode};
209              
210 0           $volume->{CLEANINGCOUNT} = $cleaningCount;
211 0           $volume->{MOUNTCOUNT} = $mountCount;
212 0           $volume->{MAXMOUNTS} = $maxMounts;
213 0 0         $volume->{GROUP} = ($volumeGroup eq "---") ? undef : $volumeGroup,
214              
215              
216             $volume->{POOL} = NBU::Pool->byID($volumePoolNumber);
217 0           $volume->{PREVIOUSPOOL} = NBU::Pool->byName($previousVolumePool);
218              
219 0 0         $volume->{OFFSITELOCATION} = $offsiteLocation unless ($offsiteLocation eq "-");
220 0 0 0       $volume->{OFFSITESLOT} = $offsiteSlot unless (($offsiteSlot eq "-") || ($offsiteSlot == 0));
221 0 0 0       $volume->{OFFSITESESSIONID} = $offsiteSessionID unless (($offsiteSessionID eq "-") || ($offsiteSessionID == 0));
222              
223 0           my $rd = $offsiteReturnDate." ".$offsiteReturnTime; $rd = str2time($rd);
  0            
224 0 0         $volume->{OFFSITERETURN} = $rd if (defined($rd));
225              
226 0           $volume->{VERSION} = $version;
227 0           $volume->{DESCRIPTION} = $description;
228              
229 0 0 0       if ($updateRobot && ($robotType ne "NONE")) {
230 0           my $robot;
231 0 0         if (!defined($robot = NBU::Robot->byID($robotNumber))) {
232 0           $robot = NBU::Robot->new($robotNumber, $robotType);
233             }
234 0           $robot->insert($slotNumber, $volume);
235 0           $volume->robot($robot);
236 0           $volume->slot($slotNumber);
237             }
238              
239 0           $volume->{NETBACKUP} = ($status == 1);
240             }
241 0           close($pipe);
242             }
243              
244 0           $pipe = NBU->cmd("bpmedialist -L |");
245 0           my $mmdbHost;
246 0           while (<$pipe>) {
247              
248 0 0         if (/^Server Host = ([\S]+)[\s]*$/) {
249 0           $mmdbHost = NBU::Host->new($1);
250 0           next;
251             }
252              
253 0 0         if (/^media_id = ([A-Z0-9]+), partner_id.*/) {
254 0 0         if ($volume) {
255 0           print STDERR "New media $1 encountered when old one ".$volume->id." still active!\n";
256 0           exit 0;
257             }
258 0           $volume = NBU::Media->byID($1);
259 0 0         if (!defined($volume)) {
260 0 0         print STDERR "Media id $1 in mmdb on ".$mmdbHost->name." was not found in any voldb!\n" if (NBU->debug);
261 0           $volume = NBU::Media->new($1);
262 0           $filled += 1;
263             }
264 0           $volume->{MMLOADED} = 1;
265 0           $volume->{MMDBHOST} = $mmdbHost;
266              
267 0           $filled += 1;
268 0           next;
269             }
270              
271 0 0         if (/^density = ([\S]+) \(([\d]+)\)/) {
272 0           $volume->{DENSITY} = $2;
273 0           next;
274             }
275 0 0         if (/^allocated = .* \(([0-9]+)\)/) {
276 0           $volume->{ALLOCATED} = $1;
277 0           next;
278             }
279 0 0         if (/^last_written = .* \(([0-9]+)\)/) {
280 0           $volume->{LASTWRITTEN} = $1;
281 0           next;
282             }
283 0 0         if (/^expiration = .* \(([0-9]+)\)/) {
284 0           $volume->{LASTIMAGEEXPIRES} = $1;
285 0           next;
286             }
287 0 0         if (/^last_read = .* \(([0-9]+)\)/) {
288 0           $volume->{LASTREAD} = $1;
289 0           next;
290             }
291              
292 0 0         if (/retention_level = ([\d]+), num_restores = ([\d]+)/) {
293 0           $volume->retention(NBU::Retention->byLevel($1));
294 0           $volume->{RESTORECOUNT} = $2;
295 0           next;
296             }
297              
298 0 0         if (/^kbytes = ([\d]+), nimages = ([\d]+), vimages = ([\d]+)/) {
299 0           $volume->{SIZE} = $1;
300 0           $volume->{IMAGECOUNT} = $2;
301 0           $volume->{VIMAGECOUNT} = $3;
302 0           next;
303             }
304              
305 0 0         if (/^status = 0x([0-9A-Fa-f]+)/) {
306 0           my $status = $1;
307 0           my $result = 0;
308 0           foreach my $d (split(/ */, $status)) {
309 0 0         $d =~ tr/a-z/A-Z/; $d = (ord($d) - ord('A') + 10) if ($d =~ /[A-F]/);
  0            
310 0           $result *= 16;
311 0           $result += $d;
312             }
313 0           $volume->{STATUS} = $result;
314 0           next;
315             }
316              
317 0 0         if (/^res1 = /) {
318 0           next;
319             }
320              
321 0 0         if (/^vmpool = /) {
322 0           next;
323             }
324              
325 0 0         if (/^[\s]*$/) {
326 0           $volume = undef;
327 0           next;
328             }
329 0           print STDERR "Unknown line\n \"$_\"\n";
330             }
331 0           close($pipe);
332             }
333              
334             my $mediaErrors = "/usr/local/etc/media-errors.csv";
335             sub loadErrors {
336 0     0 0   my $proto = shift;
337 0           my $errorCount;
338              
339 0 0         if (open(PIPE, "<$mediaErrors")) {
    0          
340             # Place this use directive inside an eval to postpone missing
341             # module diagnostics until run-time
342 0           eval "use Text::CSV_XS";
343 0           my $csv = Text::CSV_XS->new();
344              
345             # Throw the header line away and read the remaining error lines
346 0           $_ = ;
347 0           while () {
348 0 0         if ($csv->parse($_)) {
349 0           my @fields = $csv->fields;
350 0           my $volume = NBU::Media->byID($fields[1]);
351 0 0         if ($volume) {
352 0           $volume->logError($fields[0], $fields[5]);
353 0           $errorCount += 1;
354             }
355             }
356             }
357 0           close(PIPE);
358             }
359             elsif (NBU->debug) {
360 0           print STDERR "Could not load media errors from $mediaErrors\n";
361             }
362 0           return $errorCount;
363             }
364              
365             sub listIDs {
366 0     0 0   my $proto = shift;
367              
368 0           return (keys %mediaList);
369             }
370              
371             sub listVolumes {
372 0     0 0   my $proto = shift;
373              
374 0           return (values %mediaList);
375             }
376              
377             sub list {
378 0     0 0   my $proto = shift;
379              
380 0           return ($proto->listVolumes);
381             }
382              
383             sub voldbHost {
384 0     0 0   my $self = shift;
385              
386 0 0         if (@_) {
387 0           $self->{VOLDBHOST} = shift;
388             }
389 0           return $self->{VOLDBHOST};
390             }
391              
392             sub mmdbHost {
393 0     0 0   my $self = shift;
394              
395 0 0         if (@_) {
396 0           $self->{MMDBHOST} = shift;
397             }
398 0           return $self->{MMDBHOST};
399             }
400              
401             sub density {
402 0     0 0   my $self = shift;
403              
404 0 0         if (@_) {
405 0           my $density = shift;
406 0           $self->{DENSITY} = $density;
407             }
408              
409 0 0         return $self->removable ? $densities{$self->{DENSITY}} : "disk";
410             }
411              
412             sub retention {
413 0     0 0   my $self = shift;
414              
415 0 0         if (@_) {
416 0           my $retention = shift;
417 0           $self->{RETENTION} = $retention;
418             }
419              
420 0           return $self->{RETENTION};
421             }
422              
423             sub barcode {
424 0     0 0   my $self = shift;
425              
426 0 0         if (@_) {
427 0 0         if (my $oldBarcode = $self->{EVSN}) {
428 0           delete $barcodeList{$oldBarcode};
429 0           $self->{EVSN} = undef;
430             }
431 0 0         if (my $barcode = shift) {
432 0           $barcodeList{$barcode} = $self;
433 0           $self->{EVSN} = $barcode;
434             }
435             }
436 0           return $self->{EVSN};
437             }
438              
439             sub previousPool {
440 0     0 0   my $self = shift;
441              
442 0           return $self->{PREVIOUSPOOL};
443             }
444              
445             sub pool {
446 0     0 0   my $self = shift;
447              
448 0 0         if (@_) {
449 0           my $newPool = shift;
450              
451 0 0         if ((my $oldPool = $self->{POOL}) != $newPool) {
452 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
453 0           NBU->cmd("vmchange".
454             " -h ".$master->name.
455             " -m ".$self->id.
456             " -p ".$newPool->id);
457 0           $self->{PREVIOUSPOOL} = $oldPool;
458 0           $self->{POOL} = $newPool;
459             }
460             }
461 0           return $self->{POOL};
462             }
463              
464             sub group {
465 0     0 0   my $self = shift;
466              
467 0 0         if (@_) {
468 0           my $group = shift;
469 0           my $update;
470              
471 0 0         if (defined($group)) {
472 0   0       $update = !defined($self->{GROUP}) || ($group ne $self->{GROUP});
473             }
474             else {
475 0           $update = defined($self->{GROUP});
476             }
477 0 0         if ($update) {
478 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
479 0 0         NBU->cmd("vmchange".
480             " -h ".$master->name.
481             " -m ".$self->id.
482             " -new_v ".(defined($group) ? $group : "---"), 0);
483 0           $self->{GROUP} = $group;
484             }
485             }
486             else {
487 0 0         $self->populate if (!defined($filled));
488             }
489 0           return $self->{GROUP};
490             }
491              
492             sub type {
493 0     0 0   my $self = shift;
494              
495 0 0         if (@_) {
496 0           $self->{MEDIATYPE} = shift;
497             }
498              
499 0           return $self->{MEDIATYPE};
500             }
501              
502             sub logError {
503 0     0 0   my $self = shift;
504 0           my ($eDate, $eType) = @_;
505 0           $eDate = str2time($eDate);
506              
507 0 0         if (!defined($self->{ERRORHIST})) {
508 0           $self->{ERRORHIST} = {};
509             }
510 0           my $ehR = $self->{ERRORHIST};
511 0           $$ehR{$eDate} = $eType;
512              
513 0           $self->{LASTERRORDATE} = $eDate;
514 0           $self->{LASTERRORTYPE} = $eType;
515              
516 0           return $self->{ERRORCOUNT} += 1;
517             }
518              
519             sub lastError {
520 0     0 0   my $self = shift;
521              
522 0 0         if ($self->{ERRORCOUNT} > 0) {
523 0           return ($self->{LASTERRORDATE}, $self->{LASTERRORTYPE});
524             }
525             else {
526 0           return (0, undef);
527             }
528             }
529              
530             sub errorList {
531 0     0 0   my $self = shift;
532 0 0         if (!defined($self->{ERRORHIST})) {
533 0           $self->{ERRORHIST} = {};
534             }
535 0           my $ehR = $self->{ERRORHIST};
536 0           return %$ehR;
537             }
538              
539             sub errorCount {
540 0     0 0   my $self = shift;
541              
542 0           return $self->{ERRORCOUNT};
543             }
544              
545             my %cleaningTypes = (
546             12 => 1,
547             17 => 1,
548             27 => 1,
549             13 => 1,
550             15 => 1,
551             25 => 1,
552             5 => 1,
553             9 => 1,
554             23 => 1,
555             );
556             sub cleaningTape {
557 0     0 0   my $self = shift;
558              
559 0           return exists($cleaningTypes{$self->type});
560             }
561              
562             sub cleaningCount {
563 0     0 0   my $self = shift;
564              
565 0 0 0       if (@_ && $self->cleaningTape) {
566 0           my $newCount = shift;
567 0           NBU->cmd("vmchange -m ".$self->id." -n $newCount", 0);
568 0           $self->{CLEANINGCOUNT} = $newCount;
569             }
570 0           return $self->{CLEANINGCOUNT};
571             }
572              
573             sub mountCount {
574 0     0 0   my $self = shift;
575              
576 0 0         if ($self->cleaningTape) {
577 0           return $self->{CLEANINGCOUNT};
578             }
579             else {
580 0           return $self->{MOUNTCOUNT};
581             }
582             }
583              
584             sub firstMounted {
585 0     0 0   my $self = shift;
586              
587 0 0         if (@_) {
588 0 0         if (@_ > 1) {
589             # convert date and time to epoch seconds first
590             }
591             else {
592 0           $self->{FIRSTMOUNTED} = shift;
593             }
594             }
595              
596 0           return $self->{FIRSTMOUNTED};
597             }
598              
599             sub lastMounted {
600 0     0 0   my $self = shift;
601              
602 0 0         if (@_) {
603 0 0         if (@_ > 1) {
604             # convert date and time to epoch seconds first
605             }
606             else {
607 0           $self->{LASTMOUNTED} = shift;
608             }
609             }
610              
611 0           return $self->{LASTMOUNTED};
612             }
613              
614             sub byBarcode {
615 0     0 0   my $proto = shift;
616 0           my $barcode = shift;
617              
618              
619 0 0         if (my $volume = $barcodeList{$barcode}) {
620 0           return $volume;
621             }
622 0           return undef;
623             }
624              
625             #
626             # The Recorded Volume Serial Number (rvsn) is the same as the media ID hence
627             # the two variants of id and byID.
628             sub byID {
629 0     0 0   my $proto = shift;
630 0           my $mediaID = shift;
631 0           my $voldbHost = shift;
632              
633              
634 0 0         if (my $volume = $mediaList{$mediaID}) {
635 0           return $volume;
636             }
637 0           return undef;
638             }
639              
640             sub byRVSN {
641 0     0 0   my $self = shift;
642              
643 0           return $self->byID(@_);
644             }
645              
646             sub id {
647 0     0 0   my $self = shift;
648              
649 0 0         if (@_) {
650 0           $self->{RVSN} = shift;
651 0           $mediaList{$self->{RVSN}} = $self;
652             }
653              
654 0           return $self->{RVSN};
655             }
656             sub rvsn {
657 0     0 0   my $self = shift;
658              
659 0           return $self->id(@_);
660             }
661              
662             #
663             # This is the External Volume Serial Number which can sometimes be
664             # different than the Recorded Volume Serial Number (RVSN).
665             sub evsn {
666 0     0 0   my $self = shift;
667              
668 0 0         if (@_) {
669 0           $self->{EVSN} = shift;
670             }
671              
672 0           return $self->{EVSN};
673             }
674              
675             sub robot {
676 0     0 0   my $self = shift;
677              
678 0 0         if (@_) {
679 0           $self->{ROBOT} = shift;
680             }
681              
682 0           return $self->{ROBOT};
683             }
684              
685             sub slot {
686 0     0 0   my $self = shift;
687              
688 0 0         if (@_) {
689 0           $self->{SLOT} = shift;
690             }
691              
692 0           return $self->{SLOT};
693             }
694              
695             sub selected {
696 0     0 0   my $self = shift;
697              
698 0 0         if (@_) {
699 0           $self->{SELECTED} = shift;
700             }
701 0           return $self->{SELECTED};
702             }
703              
704             sub mount {
705 0     0 0   my $self = shift;
706              
707 0 0         if (@_) {
708 0           my ($mount, $drive) = @_;
709 0           $self->{MOUNT} = $mount;
710 0           $self->{DRIVE} = $drive;
711             }
712 0           return $self->{MOUNT};
713             }
714              
715             sub drive {
716 0     0 0   my $self = shift;
717              
718 0           return $self->{DRIVE};
719             }
720              
721             sub unmount {
722 0     0 0   my $self = shift;
723 0           my ($tm) = @_;
724              
725 0 0         if (my $mount = $self->mount) {
726 0           $mount->unmount($tm);
727             }
728              
729 0           $self->mount(undef, undef);
730 0           return $self;
731             }
732              
733             sub read {
734 0     0 0   my $self = shift;
735              
736 0           my ($size, $speed) = @_;
737              
738 0           $self->{SIZE} += $size;
739 0           $self->{READTIME} += ($size / $speed);
740             }
741              
742             sub write {
743 0     0 0   my $self = shift;
744              
745 0           my ($size, $speed) = @_;
746              
747 0           $self->{SIZE} += $size;
748 0           $self->{WRITETIME} += ($size / $speed);
749             }
750              
751             sub writeTime {
752 0     0 0   my $self = shift;
753              
754 0           return $self->{WRITETIME};
755             }
756              
757             sub dataWritten {
758 0     0 0   my $self = shift;
759              
760 0 0         if (@_) {
761 0           $self->{SIZE} = shift;
762             }
763 0           return $self->{SIZE};
764             }
765              
766             sub allocated {
767 0     0 0   my $self = shift;
768              
769 0 0         if (@_) {
770 0           $self->{ALLOCATED} = shift;
771             }
772              
773 0           return $self->{ALLOCATED};
774             }
775              
776             sub lastWritten {
777 0     0 0   my $self = shift;
778              
779 0 0         if (@_) {
780 0           $self->{LASTWRITTEN} = shift;
781             }
782              
783 0           return $self->{LASTWRITTEN};
784             }
785              
786             sub lastRead {
787 0     0 0   my $self = shift;
788              
789 0 0         if (@_) {
790 0           $self->{LASTREAD} = shift;
791             }
792              
793 0           return $self->{LASTREAD};
794             }
795              
796             #
797             # This refers to the date on which the youngest image on the volume expires
798             # and hence the earliest date on which the volume can be de-allocated
799             # Note to be confused with date on which the media itself expires and henceforth
800             # cano no longer be used for backups altogether.
801             sub expires {
802 0     0 0   my $self = shift;
803              
804 0 0         if (@_) {
805 0           $self->{LASTIMAGEEXPIRES} = shift;
806             }
807              
808 0           return $self->{LASTIMAGEEXPIRES};
809             }
810              
811             sub status {
812 0     0 0   my $self = shift;
813              
814 0 0         if (@_) {
815 0           $self->{STATUS} = shift;
816             }
817              
818 0           return $self->{STATUS};
819             }
820              
821             sub maxMounts {
822 0     0 0   my $self = shift;
823              
824 0 0         if (@_) {
825 0           my $maxMounts = shift;
826 0           NBU->cmd("vmchange".
827             " -m ".$self->id.
828             " -maxmounts $maxMounts", 0);
829 0           $self->{MAXMOUNTS} = $maxMounts;
830             }
831              
832 0           return $self->{MAXMOUNTS};
833             }
834              
835             sub frozen {
836 0     0 0   my $self = shift;
837              
838 0 0         return $self->allocated ? ($self->{STATUS} & 0x1) : undef;
839             }
840              
841             sub freeze {
842 0     0 0   my $self = shift;
843              
844 0 0 0       if ($self->allocated && !($self->{STATUS} & 0x1)) {
845             # issue freeze command:
846 0           NBU->cmd("bpmedia".
847             " -h ".$self->mmdbHost->name.
848             " -ev ".$self->id.
849             " -freeze\n");
850 0           $self->{STATUS} |= 0x1;
851             }
852 0           return $self;
853             }
854              
855             sub unfreeze {
856 0     0 0   my $self = shift;
857              
858 0 0 0       if ($self->allocated && ($self->{STATUS} & 0x1)) {
859             # issue unfreeze command:
860 0           NBU->cmd("bpmedia".
861             " -h ".$self->mmdbHost->name.
862             " -ev ".$self->id.
863             " -unfreeze\n");
864 0           $self->{STATUS} &= ~0x11;
865             }
866 0           return $self;
867             }
868              
869             sub suspended {
870 0     0 0   my $self = shift;
871              
872 0 0         return $self->allocated ? ($self->{STATUS} & 0x2) : undef;
873             }
874              
875             sub unsuspend {
876 0     0 0   my $self = shift;
877              
878 0 0 0       if ($self->allocated && ($self->{STATUS} & 0x2)) {
879             # issue unfreeze command:
880 0           NBU->cmd("bpmedia".
881             " -h ".$self->mmdbHost->name.
882             " -ev ".$self->id.
883             " -unfreeze\n");
884 0           $self->{STATUS} &= ~0x2;
885             }
886 0           return $self;
887             }
888              
889             #
890             # If a Media Manager allocates a volume only to fail to write to it,
891             # it is possible for the volume to be frozen without having any data
892             # written to it, i.e. it does not even have a valid header. This state of
893             # the volume is identified by status bit 4.
894             # This author has only observed this bit in conjunction with bit 0. As a
895             # matter of fact, unfreezing such a volume will also remove bit 4
896             sub unmountable {
897 0     0 0   my $self = shift;
898              
899 0 0         return (defined($self->{STATUS}) ? $self->{STATUS} & 0x10 : 0);
900             }
901              
902             sub multipleRetentions {
903 0     0 0   my $self = shift;
904              
905 0 0         return (defined($self->{STATUS}) ? $self->{STATUS} & 0x40 : 0);
906             }
907              
908             sub imported {
909 0     0 0   my $self = shift;
910              
911 0 0         return (defined($self->{STATUS}) ? $self->{STATUS} & 0x80 : 0);
912             }
913              
914             sub mpx {
915 0     0 0   my $self = shift;
916              
917 0 0         return (defined($self->{STATUS}) ? $self->{STATUS} & 0x200 : 0);
918             }
919              
920             sub offsiteSessionID {
921 0     0 0   my $self = shift;
922              
923 0 0         if (@_) {
924 0           my $offsiteSessionID = shift;
925 0           my $update;
926              
927 0 0         if (defined($offsiteSessionID)) {
928 0   0       $update = !defined($self->{OFFSITESESSIONID}) || ($offsiteSessionID ne $self->{OFFSITESESSIONID});
929             }
930             else {
931 0           $update = defined($self->{OFFSITESESSIONID});
932             }
933 0 0         if ($update) {
934 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
935 0 0         NBU->cmd("vmchange".
936             " -h ".$master->name.
937             " -m ".$self->id.
938             " -offsid ".(defined($offsiteSessionID) ? $offsiteSessionID : "-"), 0);
939 0           $self->{OFFSITESESSIONID} = $offsiteSessionID;
940             }
941             }
942 0           return $self->{OFFSITESESSIONID};
943             }
944              
945             sub offsiteReturnDate {
946 0     0 0   my $self = shift;
947              
948 0 0         if (@_) {
949 0           my $offsiteReturnDate = shift;
950 0           my $update;
951              
952 0 0         if (defined($offsiteReturnDate)) {
953 0   0       $update = !defined($self->{OFFSITERETURN}) || ($offsiteReturnDate ne $self->{OFFSITERETURN});
954             }
955             else {
956 0           $update = defined($self->{OFFSITERETURN});
957             }
958 0 0         if ($update) {
959 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
960 0 0         NBU->cmd("vmchange".
961             " -h ".$master->name.
962             " -m ".$self->id.
963             " -offreturn ".(defined($offsiteReturnDate) ? NBU->date($offsiteReturnDate) : "0"), 0);
964 0           $self->{OFFSITERETURN} = $offsiteReturnDate;
965             }
966             }
967 0           return $self->{OFFSITERETURN};
968             }
969              
970             sub offsiteSentDate {
971 0     0 0   my $self = shift;
972              
973 0 0         if (@_) {
974 0           my $offsiteSentDate = shift;
975 0           my $update;
976              
977 0 0         if (defined($offsiteSentDate)) {
978 0   0       $update = !defined($self->{OFFSITESENT}) || ($offsiteSentDate ne $self->{OFFSITESENT});
979             }
980             else {
981 0           $update = defined($self->{OFFSITESENT});
982             }
983 0 0         if ($update) {
984 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
985 0 0         NBU->cmd("vmchange".
986             " -h ".$master->name.
987             " -m ".$self->id.
988             " -offsent ".(defined($offsiteSentDate) ? NBU->date($offsiteSentDate) : "0"), 0);
989 0           $self->{OFFSITESENT} = $offsiteSentDate;
990             }
991             }
992 0           return $self->{OFFSITESENT};
993             }
994              
995             sub offsiteLocation {
996 0     0 0   my $self = shift;
997              
998 0 0         if (@_) {
999 0           my $offsiteLocation = shift;
1000 0           my $update;
1001              
1002 0 0         if (defined($offsiteLocation)) {
1003 0   0       $update = !defined($self->{OFFSITELOCATION}) || ($offsiteLocation ne $self->{OFFSITELOCATION});
1004             }
1005             else {
1006 0           $update = defined($self->{OFFSITELOCATION});
1007             }
1008 0 0         if ($update) {
1009 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
1010 0 0         NBU->cmd("vmchange".
1011             " -h ".$master->name.
1012             " -m ".$self->id.
1013             " -offloc ".(defined($offsiteLocation) ? $offsiteLocation : "-"), 0);
1014 0           $self->{OFFSITELOCATION} = $offsiteLocation;
1015             }
1016             }
1017 0           return $self->{OFFSITELOCATION};
1018             }
1019              
1020             sub offsiteSlot {
1021 0     0 0   my $self = shift;
1022              
1023 0 0         if (@_) {
1024 0           my $offsiteSlot = shift;
1025 0           my $update;
1026              
1027 0 0         if (defined($offsiteSlot)) {
1028 0   0       $update = !defined($self->{OFFSITESLOT}) || ($offsiteSlot ne $self->{OFFSITESLOT});
1029             }
1030             else {
1031 0           $update = defined($self->{OFFSITESLOT});
1032             }
1033 0 0         if ($update) {
1034 0           my @masters = NBU->masters; my $master = $masters[0];
  0            
1035 0 0         NBU->cmd("vmchange".
1036             " -h ".$master->name.
1037             " -m ".$self->id.
1038             " -offslot ".(defined($offsiteSlot) ? $offsiteSlot : "-"), 0);
1039 0           $self->{OFFSITESLOT} = $offsiteSlot;
1040             }
1041             }
1042 0           return $self->{OFFSITESLOT};
1043             }
1044              
1045             #
1046             # Return true, that is a non-zero value, if the tape is indeed full.
1047             sub full {
1048 0     0 0   my $self = shift;
1049              
1050 0 0         return (defined($self->{STATUS}) ? $self->{STATUS} & 0x8 : 0);
1051             }
1052              
1053             #
1054             # Is the volume allocated to backing up NetBackup itself? If not, then
1055             # it is "available" to the media managers
1056             sub netbackup {
1057 0     0 0   my $self = shift;
1058              
1059 0           return $self->{NETBACKUP};
1060             }
1061             sub available {
1062 0     0 0   my $self = shift;
1063              
1064 0           return !$self->{NETBACKUP};
1065             }
1066              
1067             #
1068             # The particular value returned is the number of seconds elapsed since
1069             # the tape was taken into service (ALLOCATED) and when it was last written.
1070             # Think of this as the volume's retirement age :-)
1071             # Note that this value can in fact be zero even if the tape is full.
1072             sub fillTime {
1073 0     0 0   my $self = shift;
1074              
1075 0 0         return $self->full ? ($self->{LASTWRITTEN} - $self->{ALLOCATED}) : undef;
1076             }
1077              
1078             sub eject {
1079 0     0 0   my $self = shift;
1080              
1081 0 0         if ($self->robot) {
1082 0           NBU->cmd("vmchange -res"." -m ".$self->id." -mt ".$self->id.
1083             " -rn ".$self->robot->id." -rc1 ".$self->slot.
1084             " -rh ".$self->robot->host->name.
1085             " -e -sec 1", 0);
1086 0           return $self;
1087             }
1088             else {
1089 0           return undef;
1090             }
1091             }
1092              
1093             sub removable {
1094 0     0 0   my $self = shift;
1095              
1096 0 0         return (defined($self->{REMOVABLE}) ? $self->{REMOVABLE} : 0);
1097             }
1098              
1099             #
1100             # Insert a single fragment into this volume's table of contents
1101             sub insertFragment {
1102 0     0 0   my $self = shift;
1103 0           my $index = shift;
1104 0           my $fragment = shift;
1105            
1106             #
1107             # The table of contents has one entry per file on the tape
1108 0 0         $self->{TOC} = [] if (!defined($self->{TOC}));
1109 0           my $toc = $self->{TOC};
1110              
1111             #
1112             # Non-removable media means disk storage unit "media". These only
1113             # contain a single fragment so we force the index to zero.
1114 0 0         $index = 0 if (!$self->removable);
1115              
1116             #
1117             # In turn, a file on the tape can contain multiple fragments of images
1118             # whenever multiplexing is enabled, hence we keep so-called mpx lists for each
1119             # file.
1120 0 0         $$toc[$index] = [] if (!defined($$toc[$index]));
1121 0           my $mpxList = $$toc[$index];
1122 0           push @$mpxList, $fragment;
1123             }
1124              
1125             #
1126             # Load the list of fragments for this volume into its table of
1127             # contents.
1128             sub loadImages {
1129 0     0 0   my $self = shift;
1130              
1131 0 0         $self->{TOC} = [] if (!defined($self->{TOC}));
1132              
1133 0 0 0       if (!$self->{MMLOADED} || ($self->allocated && ($self->expires > time))) {
      0        
1134 0           NBU::Image->loadImages(NBU->cmd("bpimmedia -l -mediaid ".$self->id." |"));
1135             }
1136 0           return $self->{TOC};
1137             }
1138              
1139             sub tableOfContents {
1140 0     0 0   my $self = shift;
1141              
1142 0 0         if (!defined($self->{TOC})) {
1143 0           $self->loadImages;
1144             }
1145              
1146 0           my $toc = $self->{TOC};
1147 0           return (@$toc);
1148             }
1149              
1150             1;
1151              
1152             __END__