File Coverage

blib/lib/NBU/Drive.pm
Criterion Covered Total %
statement 22 258 8.5
branch 0 100 0.0
condition 0 21 0.0
subroutine 6 37 16.2
pod 0 31 0.0
total 28 447 6.2


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::Drive;
7              
8 1     1   5 use strict;
  1         2  
  1         30  
9 1     1   4 use Carp;
  1         2  
  1         50  
10              
11             BEGIN {
12 1     1   4 use Exporter ();
  1         2  
  1         24  
13 1     1   4 use AutoLoader qw(AUTOLOAD);
  1         1  
  1         6  
14 1     1   37 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
  1         2  
  1         155  
15 1     1   2 $VERSION = do { my @r=(q$Revision: 1.24 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
  1         8  
  1         9  
16 1         14 @ISA = qw();
17 1         2 @EXPORT = qw();
18 1         1 @EXPORT_OK = qw();
19 1         2858 %EXPORT_TAGS = qw();
20             }
21              
22             #
23             # Drive indices are unique only local to a media manager, hence
24             # the key into the Index pool has to be a combination of
25             # index and host.
26             # Same for drive names.
27             my %driveIndexPool;
28             my %driveNamePool;
29              
30             sub new {
31 0     0 0   my $proto = shift;
32 0           my $drive = {};
33              
34 0           bless $drive, $proto;
35              
36 0 0         if (@_) {
37 0           my $index = shift;
38 0           $drive->{INDEX} = $index;
39              
40 0           my $mmHost = shift;
41 0 0         if (!defined($mmHost)) {
42 0           $mmHost = NBU::Host->new("localhost");
43             }
44              
45 0           $driveIndexPool{$mmHost->name.":".$index} = $drive;
46              
47 0           $drive->{STATUS} = "DOWN";
48 0           $drive->{CONTROL} = "DOWN-TLD";
49 0           $drive->{HANDLERS} = {};
50             }
51 0           return $drive;
52             }
53              
54             #
55             # Drive objects return 2, just as Media Manager storage units do
56             sub type {
57 0     0 0   my $self = shift;
58              
59 0           return 2;
60             }
61              
62             sub byIndex {
63 0     0 0   my $proto = shift;
64 0           my $index = shift;
65 0           my $mmHost = shift;
66 0           my $drive;
67              
68 0 0         if (!defined($mmHost)) {
69 0           $mmHost = NBU::Host->new("localhost");
70             }
71              
72 0 0 0       if (defined($index) && !($drive = $driveIndexPool{$mmHost->name.":".$index})) {
73 0           $drive = NBU::Drive->new($index, $mmHost);
74             }
75 0           return $drive;
76             }
77              
78             sub byName {
79 0     0 0   my $proto = shift;
80 0           my $driveName = shift;
81 0           my $drive;
82              
83 0 0         $drive = $driveNamePool{$driveName} if (defined($driveName));
84 0           return $drive;
85             }
86              
87             my %driveHosts;
88             sub populate {
89 0     0 0   my $proto = shift;
90 0           my $server = shift;
91              
92 0 0         return if (!$server->roboticMediaManager());
93              
94 0 0         if (!exists($driveHosts{$server->name})) {
95 0           my $driveCount = 0;
96 0           my $pipe = NBU->cmd("vmoprcmd -h ".$server->name." -xdraw ds |");
97 0           while (<$pipe>) {
98 0 0         next unless (/^DRIVESTATUS/);
99 0           my ($marker, $index, $density, $control, $user, $rvsn, $evsn, $request,
100             $robotType, $robotNumber, $state, $name, $assigned, $ignore2, $lastCleaned, $comment
101             ) = split(/\s+/, $_, 16);
102 0           chop $comment;
103              
104 0           my $drive = NBU::Drive->byIndex($index, $server);
105 0 0         if ($robotType ne "-") {
106 0           my $robot = NBU::Robot->new($robotNumber, $robotType);
107 0           $drive->{ROBOT} = $robot; $robot->controlDrive($drive);
  0            
108             }
109 0           $drive->{HOST} = $server;
110 0           $driveNamePool{$drive->{NAME} = $name} = $drive;
111 0           $drive->{LASTCLEANED} = $lastCleaned;
112 0           $drive->{COMMENT} = $comment;
113              
114 0           $drive->{CONTROL} = $control;
115 0 0         $drive->{STATUS} = ($control !~ /DOWN/) ? "UP" : "DOWN";
116              
117 0           $drive->{INUSE} = ($state & 0x000f);
118              
119             #
120             # If the drive is busy, and the media has no recorded ID yet, but it
121             # does have an external ID, then this media is being used for the first
122             # time and it will have its external ID recorded henceforth. So here we
123             # simply jump the gun a little...
124 0 0 0       if ($drive->busy && ($rvsn eq "-") && ($evsn ne "-")) {
      0        
125 0           $rvsn = $evsn;
126             }
127 0 0         if ($rvsn ne "-") {
128             # Unfortunately there is no way to find out which job is using this drive :-(
129 0 0         if (defined(my $volume = NBU::Media->new($rvsn))) {
  0            
130 0           my $mount = NBU::Mount->new(undef, $volume, $drive, time);
131 0           $drive->use($mount, time);
132             }
133             else {die("Cannot located media $rvsn?\n");}
134             }
135              
136 0           $driveCount++;
137             }
138 0           close($pipe);
139 0           $driveHosts{$server->name} = $driveCount;
140             }
141 0           return $driveHosts{$server->name};
142             }
143              
144             #
145             # Try to get more detail on this drive.
146             # If the host to query is not specified, first look to the drive's
147             # host or else the local master server for this information
148             sub loadDriveDetail {
149 0     0 0   my $self = shift;
150 0           my $server = shift;
151              
152 0 0         if (!defined($server)) {
153 0           $server = $self->host;
154             }
155 0 0         if (!defined($server)) {
156 0           my @masters = NBU->masters; $server = $masters[0];
  0            
157             }
158              
159 0           my $pipe = NBU->cmd("vmglob -h ".$server->name." -listall -java |");
160 0           while (<$pipe>) {
161 0 0         next unless (/VMGLOB... drive /);
162 0           chop;
163              
164 0           my($key, $d, $driveName, $serial, $host, $volumeDBHost, $robotNumber, $robotDriveIndex, $density, $flags, $wwName) =
165             split;
166              
167 0 0         if (my $drive = NBU::Drive->byName($driveName)) {
168 0           $drive->{SERIALNUMBER} = $serial;
169 0           $drive->{ROBOTDRIVEINDEX} = $robotDriveIndex;
170 0 0         $drive->{WWNAME} = $wwName if ($wwName ne "-");
171              
172 0           $drive->{DETAILED} = 1;
173             }
174             }
175 0           $self->{DETAILED} = 1;
176             }
177              
178             #
179             # Here known refers to whether the drive is known to NetBackup, i.e. whether
180             # it is part of a storage unit definition and thus could be used for backups.
181             sub known {
182 0     0 0   my $self = shift;
183              
184 0 0         if (defined(my $stu = $self->{STU})) {
    0          
185 0           return ($stu);
186             }
187             elsif (defined(my $robot = $self->robot)) {
188 0           return ($robot->known);
189             }
190 0           return ();
191             }
192              
193             sub updateStatus {
194 0     0 0   my $proto = shift;
195 0           my $server = shift;
196              
197             #
198             # Only applies to robotic media managers
199 0 0         return if (!$server->roboticMediaManager());
200              
201 0           my $pipe = NBU->cmd("vmoprcmd -h ".$server->name." -xdraw ds |");
202 0           while (<$pipe>) {
203 0 0         next unless (/^DRIVESTATUS/);
204 0           my ($ignore, $id, $density, $control, $user, $rvsn, $evsn, $request,
205             $robotType, $robotNumber, $state, $name, $assigned, $ignore2, $lastCleaned, $comment
206             ) = split(/\s+/, $_, 16);
207 0           chop $comment;
208              
209 0           my $drive = NBU::Drive->byIndex($id, $server);
210              
211 0           $drive->comment($comment);
212 0           $drive->{LASTCLEANED} = $lastCleaned;
213              
214 0           $drive->{INUSE} = ($state & 0x000f);
215              
216 0           $drive->status($control);
217              
218 0 0 0       if ($drive->busy) {
    0          
219 0           my $mount = $drive->mount;
220 0 0 0       if (($rvsn eq "-") && ($evsn ne "-")) {
221 0           $rvsn = $evsn;
222             }
223              
224 0 0         if ($rvsn eq "-") {
    0          
225 0           $drive->free(time);
226             }
227             elsif ($mount->volume->rvsn ne $rvsn) {
228 0           $drive->free(time);
229 0           $mount = NBU::Mount->new(undef, NBU::Media->new($rvsn), $drive, time);
230 0           $drive->use($mount, time);
231             }
232             }
233             elsif (!$drive->busy && ($rvsn ne "-")) {
234 0           my $mount = NBU::Mount->new(undef, NBU::Media->new($rvsn), $drive, time);
235 0           $drive->use($mount, time);
236             }
237             }
238 0           close($pipe);
239             }
240              
241             sub pool {
242 0     0 0   my $proto = shift;
243              
244 0           return (values %driveIndexPool);
245             }
246              
247             sub status {
248 0     0 0   my $self = shift;
249              
250 0 0         if (@_) {
251 0           my $oldStatus = $self->{STATUS};
252 0           my $control = $self->{CONTROL} = shift;
253 0 0         my $newStatus = ($control !~ /DOWN/) ? "UP" : "DOWN";
254              
255 0 0         if ($oldStatus ne $newStatus) {
256 0           my $handlers = $self->{HANDLERS};
257              
258 0 0         if (exists($$handlers{$newStatus})) {
259 0           my $handler = $$handlers{$newStatus};
260 0           &$handler($self, $newStatus);
261             }
262             }
263              
264 0           $self->{STATUS} = $newStatus;
265             }
266 0           return $self->{STATUS};
267             }
268              
269             sub control {
270 0     0 0   my $self = shift;
271              
272 0           return $self->{CONTROL};
273             }
274              
275              
276             #
277             # If the drive is not up, try to change its state to up. Then,
278             # if an argument is provided the host will be queried to see if the
279             # drive indeed came back up. Without argument the code assumes all
280             # went as planned.
281             sub up {
282 0     0 0   my $self = shift;
283              
284              
285 0 0         if (@_) {
286 0 0         if ($self->{STATUS} ne "UP") {
287 0           system($NBU::prefix."volmgr/bin/vmoprcmd -up ".$self->id." -h ".$self->host->name."\n");
288 0           $self->updateStatus($self->host);
289             }
290             }
291              
292 0           return $self->{STATUS} eq "UP";
293             }
294              
295             #
296             # Same story as the up routine just above
297             sub down {
298 0     0 0   my $self = shift;
299              
300 0 0         if (@_) {
301 0 0         if ($self->{STATUS} ne "DOWN") {
302 0           system($NBU::prefix."volmgr/bin/vmoprcmd -down ".$self->id." -h ".$self->host->name."\n");
303 0           $self->updateStatus($self->host);
304             }
305             }
306              
307 0           return $self->{STATUS} eq "DOWN";
308             }
309              
310             sub busy {
311 0     0 0   my $self = shift;
312              
313 0           return $self->{INUSE};
314             }
315              
316             sub index {
317 0     0 0   my $self = shift;
318              
319 0           return $self->id(@_);
320             }
321              
322             sub id {
323 0     0 0   my $self = shift;
324              
325 0 0         if (@_) {
326 0           my $index = shift;
327 0           $self->{INDEX} = $index;
328              
329 0           my $mmHost = shift;
330 0 0         if (!defined($mmHost)) {
331 0           $mmHost = NBU::Host->new("localhost");
332             }
333 0           $driveIndexPool{$mmHost->name.":".$index} = $self;
334             }
335              
336 0           return $self->{INDEX};
337             }
338              
339             sub comment {
340 0     0 0   my $self = shift;
341              
342 0 0         if (@_) {
343 0           $self->{COMMENT} = shift;
344             }
345              
346 0           return $self->{COMMENT};
347             }
348              
349             sub name {
350 0     0 0   my $self = shift;
351              
352 0           return $self->{NAME};
353             }
354              
355             sub serialNumber {
356 0     0 0   my $self = shift;
357              
358 0 0         $self->loadDriveDetail if (!defined($self->{DETAILED}));
359 0           return $self->{SERIALNUMBER};
360             }
361              
362             sub worldWideName {
363 0     0 0   my $self = shift;
364              
365 0 0         $self->loadDriveDetail if (!defined($self->{DETAILED}));
366 0           return $self->{WWNAME};
367             }
368              
369             sub host {
370 0     0 0   my $self = shift;
371              
372 0           return $self->{HOST};
373             }
374              
375             sub use {
376 0     0 0   my $self = shift;
377 0           my ($mount, $tm) = @_;
378              
379 0 0         if ($self->{INUSE}) {
380 0           $self->free($tm);
381             }
382              
383 0           my $uses = $self->usage;
384              
385 0           my %use;
386 0           $use{'MOUNT'} = $mount;
387 0           $mount->usedBy(\%use);
388              
389 0           $self->{INUSE} = $use{'START'} = $tm;
390 0           push @$uses, \%use;
391 0           return $self;
392             }
393              
394             sub mount {
395 0     0 0   my $self = shift;
396              
397 0 0         if ($self->busy) {
398 0           my $uses = $self->usage;
399 0           my $use = $$uses[@$uses - 1];
400 0           return $$use{'MOUNT'};
401             }
402              
403 0           return undef;
404             }
405              
406             sub free {
407 0     0 0   my $self = shift;
408 0           my $tm = shift;
409              
410 0 0         if (!$self->{INUSE}) {
411             # it is quite common for a mount to inform the drive it is no
412             # longer using the drive sometime after the drive has been put
413             # to new use already. Hence ignore this event.
414             # print "Drive ".$self->id." already free!\n";
415             # exit(0);
416             }
417 0           $self->{INUSE} = undef;
418              
419 0           my $uses = $self->usage;
420 0           my $use = pop @$uses;
421 0           $$use{'STOP'} = $tm;
422 0           push @$uses, $use;
423              
424 0           return $self;
425             }
426              
427             sub robot {
428 0     0 0   my $self = shift;
429              
430 0           return $self->{ROBOT};
431             }
432              
433             sub robotDriveIndex {
434 0     0 0   my $self = shift;
435              
436 0 0         $self->loadDriveDetail if (!defined($self->{DETAILED}));
437 0           return $self->{ROBOTDRIVEINDEX};
438             }
439              
440             sub lastCleaned {
441 0     0 0   my $self = shift;
442              
443 0           return $self->{LASTCLEANED};
444             }
445              
446             sub lastUsed {
447 0     0 0   my $self = shift;
448              
449 0           my $uses = $self->usage;
450 0 0         if (my $use = pop @$uses) {
451 0           return $$use{'START'};
452             }
453             else {
454 0           return 0;
455             }
456             }
457              
458             sub usage {
459 0     0 0   my $self = shift;
460              
461 0 0         if (!$self->{USES}) {
462 0           $self->{USES} = [];
463             }
464              
465 0           return $self->{USES};
466             }
467              
468             sub busyStats {
469 0     0 0   my $self = shift;
470 0           my $asOf = shift;
471 0           my $endOfPeriod = shift;
472              
473 0           my $stepSize = 5 * 60;
474 0 0         $stepSize = shift if (@_);
475              
476 0           my $usage = $self->usage;
477              
478 0           my $step = $asOf;
479 0           my $use = shift @$usage;
480 0           my $mount = $$use{MOUNT};
481 0           my $job = $mount->job;
482 0           my $du = 1;
483              
484 0           my @driveInUse;
485 0           while ($step < $endOfPeriod) {
486 0 0 0       if (!defined($use) || ($step < $$use{START})) {
    0          
487 0           push @driveInUse, 0;
488             }
489             elsif ($step < $$use{STOP}) {
490 0           push @driveInUse, $du;
491             }
492             else {
493 0           $use = shift @$usage;
494 0 0 0       if (defined($use) && defined($mount = $$use{MOUNT})) {
495 0           $du = 1;
496             }
497             else {
498 0           $du = 0;
499             }
500 0           next;
501             }
502 0           $step += $stepSize;
503             }
504              
505 0           return ($asOf, $endOfPeriod, $stepSize, @driveInUse);
506             }
507              
508             sub notifyOn {
509 0     0 0   my $self = shift;
510 0           my $target = shift;
511 0           my $handler = shift;
512              
513 0           my $handlers = $self->{HANDLERS};
514 0           $$handlers{$target} = $handler;
515             }
516              
517             1;
518              
519             __END__