File Coverage

blib/lib/NBU/Job.pm
Criterion Covered Total %
statement 25 491 5.0
branch 0 370 0.0
condition 0 54 0.0
subroutine 7 69 10.1
pod 0 62 0.0
total 32 1046 3.0


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::Job;
7              
8 1     1   6 use Time::Local;
  1         3  
  1         69  
9              
10 1     1   6 use strict;
  1         3  
  1         38  
11 1     1   5 use Carp;
  1         3  
  1         65  
12              
13             BEGIN {
14 1     1   6 use Exporter ();
  1         2  
  1         21  
15 1     1   5 use AutoLoader qw(AUTOLOAD);
  1         2  
  1         8  
16 1     1   45 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
  1         2  
  1         158  
17 1     1   2 $VERSION = do { my @r=(q$Revision: 1.64 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
  1         17  
  1         10  
18 1         11 @ISA = qw();
19 1         2 @EXPORT = qw();
20 1         2 @EXPORT_OK = qw();
21 1         8626 %EXPORT_TAGS = qw();
22             }
23              
24             my %pids;
25             my %jobs;
26              
27             #
28             # New jobs are registered under their Process IDs
29             sub new {
30 0     0 0   my $Class = shift;
31 0           my $job = {
32             MOUNTLIST => {},
33             SIZE => 0,
34             };
35              
36 0           bless $job, $Class;
37              
38 0 0         if (@_) {
39 0           $job->{PID} = shift;
40              
41 0 0         if (exists($pids{$job->pid})) {
42 0           my $pidArray = $pids{$job->pid};
43 0           push @$pidArray, $job;
44             }
45             else {
46 0           $pids{$job->pid} = [ $job ];
47             }
48             }
49 0           return $job;
50             }
51              
52             sub readerPID {
53 0     0 0   my $self = shift;
54              
55 0 0         if (@_) {
56 0           my $reader = shift;
57 0           $self->{READERPID} = $reader;
58 0 0         if (exists($pids{$reader})) {
59 0           my $pidArray = $pids{$reader};
60 0           push @$pidArray, $self;
61             }
62             else {
63 0           $pids{$reader} = [ $self ];
64             }
65             }
66 0           return $self->{READERPID};
67             }
68              
69             #
70             # Extract all jobs from the hash and return them
71             # in a simple array
72             sub list {
73 0     0 0   my $Class = shift;
74              
75 0           return (values %jobs);
76             }
77              
78             my @jobTypes = ("Backup", "Archive", "Restore", undef, "Duplicate", "Import", "Catalog", "Vault", undef, undef, undef, undef, undef, undef, undef, undef, undef, "Image Cleanup");
79             my $asOf;
80             my $fromFile = $ENV{"HOME"}."/.alljobs.allcolumns";
81             my ($jobPipe, $refreshPipe);
82             sub loadJobs {
83 0     0 0   my $Class = shift;
84 0           my $master = shift;
85 0           my $readFromFile = shift;
86 0           my $logFile = shift;
87 0           my $alternateFromFile = shift;
88              
89 0 0         if (defined($readFromFile)) {
90 0 0         my $file = defined($alternateFromFile) ? $alternateFromFile : $fromFile;
91 0 0         die "Cannot open previous job log file \"$file\"\n" unless open(PIPE, "<$file");
92              
93 0 0         if (NBU->debug) {
94 0           print STDERR "Reading: job history file $file\n";
95             }
96 0           $jobPipe = *PIPE{IO};
97 0           my @stat = stat(PIPE); $asOf = $stat[9];
  0            
98             }
99             else {
100 0           $asOf = time;
101 0 0         my $tee = defined($logFile) ? "| tee $fromFile" : "";
102 0           ($jobPipe, $refreshPipe) = NBU->cmd("| bpdbjobs -report -all_columns -stay_alive -M ".$master->name." $tee |");
103             }
104              
105 0 0         if (!(<$jobPipe> =~ /^C([\d]+)[\s]*$/)) {
106 0           return undef;
107             }
108 0           my $jobRowCount = $1;
109              
110 0           while ($jobRowCount--) {
111 0           my $jobDescription;
112 0 0         if (!($jobDescription = <$jobPipe>)) {
113 0           print STDERR "Failed to read from job pipe ($jobPipe) when $jobRowCount jobs were yet expected...\n";
114 0           last;
115             }
116 0           parseJob($master, $jobDescription);
117             }
118              
119 0           return $asOf;
120             }
121              
122             sub refreshJobs {
123 0     0 0   my $Class = shift;
124 0           my $master = shift;
125              
126 0 0         return undef if (!defined($jobPipe));
127              
128 0 0         print $refreshPipe "refresh\n" if (defined($refreshPipe));
129              
130 0 0         if (!(<$jobPipe> =~ /^C([\d]+)[\s]*$/)) {
131 0           return undef;
132             }
133 0           my $jobRowCount = $1;
134              
135 0           while ($jobRowCount--) {
136 0           my $jobDescription;
137 0 0         if (!($jobDescription = <$jobPipe>)) {
138 0           print STDERR "Failed to read from job pipe ($jobPipe)\n";
139 0           last;
140             }
141 0           parseJob($master, $jobDescription);
142             }
143              
144 0           return $asOf;
145             }
146              
147             sub parseJob {
148 0     0 0   my $master = shift;
149 0           my $jobDescription = shift;
150 0           chop $jobDescription;
151              
152             #
153             # Just after midnight, a "refresh" request on active bpdbjobs connection will result in
154             # aging out a number of jobs. This is communicated by sending the jobid's preceded by a minus
155             # sign. Such events are ignored here:
156 0 0         if ($jobDescription =~ /^\-/) {
157 0           return;
158             }
159              
160             #
161             # Occasionally some well-meaning but severely misguided soul decides that
162             # the occasional comma inserted in the midst of an error message is a bad
163             # thing indeed (which it is) so it was decided to quote that comma with a
164             # back-slash. It is for occasions such as this that the expression "From
165             # the frying pan into the fire" was invented. 'nuff said.
166 0 0         if ($jobDescription =~ s/([^\\])\\,/${1} -/g) {
167             }
168              
169 0           my $KBWritten = 0;
170             my (
171 0           $jobID, $jobType, $state, $status, $className, $scheduleName, $clientName,
172             $serverName, $started, $elapsed, $ended, $stUnit, $currentTry, $operation,
173             $KBytesWritten, $filesWritten, $currentFile, $percent,
174             # This is the PID of the bpsched process on the master
175             $jobPID,
176             $owner,
177             $subType, $classType, $scheduleType, $priority,
178             $group, $masterServer, $retentionUnits, $retentionPeriod,
179             $compression,
180             # The next two values are used to compute % complete information, i.e.
181             # they represent historical data
182             $KBytesLastWritten, $filesLastWritten,
183             $pathListCount,
184             @rest) = split(/,/, $jobDescription);
185              
186 0           my $job;
187 0 0         if (!($job = NBU::Job->byID($jobID))) {
188 0           $job = NBU::Job->new($jobPID);
189 0           $job->id($jobID);
190              
191              
192 0           $job->start($started);
193              
194 0 0         $job->{TYPE} = $jobTypes[$jobType] if ($jobType ne "");
195 0 0         if (NBU->debug) {
196 0 0         print STDERR "Undefined job type \"$jobType\" for job $jobID\n" if (!defined($job->{TYPE}));;
197             }
198              
199 0 0 0       $job->{STUNIT} = NBU::StorageUnit->byLabel($stUnit) if (defined($stUnit) && ($stUnit !~ /^[\s]*$/));
200 0           my $backupID = $clientName."_".$started;
201 0           my $image = $job->image($backupID);
202 0           $job->{CLASS} = my $class = $image->class(NBU::Class->new($className, $classType, $master));
203 0           $job->{SCHEDULE} = $image->schedule(NBU::Schedule->new($class, $scheduleName, $scheduleType));
204 0           $job->{CLIENT} = $image->client(NBU::Host->new($clientName));
205             }
206              
207             #
208             # Record a job's media server at the earliest opportunity
209 0 0 0       if (!defined($job->mediaServer) && defined($serverName)) {
210 0           $job->mediaServer(NBU::Host->new($serverName));
211 0 0 0       $job->{STUNIT} = NBU::StorageUnit->byLabel($stUnit) if (defined($stUnit) && ($stUnit !~ /^[\s]*$/));
212             }
213              
214 0           $job->state($state);
215 0 0         $job->{TRY} = ($currentTry ne "") ? $currentTry : undef;
216              
217             #
218             # Extract the list of paths (either in the class definition's include list
219             # or the ones provided by the user.
220             # As of NBU 6.5 (possibly earlier), the path descriptions have gotten more verbose.
221             # Mostly this is a good thing, except that under certain circumstances these
222             # descriptions now contain commas! Specifically, when we see a reference to FITYPE
223             # we automatically assume an FSTYPE clause followed and we pull it from the list of items
224             # in situ.
225             # Other times commas embedded in paths are escaped with a backslash. As long as the path
226             # being constructed ends in a backslash, replace the comma and pull the next item from the
227             # list.
228 0           my @paths;
229 0 0         if (defined($pathListCount)) {
230 0           for my $i (1..$pathListCount) {
231 0           my $p = shift @rest;
232 0   0       while (($p =~ /\\$/) && ($p !~ /[^\\]\\\\$/)) {
233 0           $p .= ",".shift @rest;
234             }
235 0           push @paths, $p;
236             }
237             }
238 0           $job->{FILES} = \@paths;
239              
240             #
241             # March through the list of tries and for each of them, extract the progress
242             # scenario. Need to think about a way to do delta's: remember last try and
243             # progress indices perhaps?
244 0 0         if (defined(my $tryCount = shift @rest)) {
245 0           for my $i (1..$tryCount) {
246 0           my ($tryPID, $tryStUnit, $tryServer,
247             $tryStarted, $tryElapsed, $tryEnded,
248             $tryStatus, $description, $tryProgressCount, @tryRest) = @rest;
249              
250            
251 0           my $backupID = $job->client."_".$tryStarted;
252 0           my $image = $job->image($backupID);
253 0           $image->class($job->class);
254 0           $image->schedule($job->schedule);
255 0           $image->client($job->client);
256              
257 0           $elapsed = $tryElapsed;
258 0           for my $t (1..$tryProgressCount) {
259 0           my $tryProgress = shift @tryRest;
260              
261 0 0         if ($tryProgress =~ /\.\.\./) {
262 0           next;
263             }
264              
265 0           my ($dt, $tm, $AMPM, $dash, $msg);
266 0 0         if ($tryProgress =~ /[\s][AP]M[\s]/) {
267 0           ($dt, $tm, $AMPM, $dash, $msg) = split(/[\s]+/, $tryProgress, 5);
268             }
269             else {
270 0           ($dt, $tm, $dash, $msg) = split(/[\s]+/, $tryProgress, 4);
271 0           $AMPM = "";
272             }
273 0           my $mm; my $dd; my $yyyy;
  0            
274 0 0         if ($dt =~ /([\d]{1,2})\/([\d]{1,2})\/([\d]{4})/) {
    0          
275 0           $yyyy = $3;
276             }
277             elsif ($dt =~ /([\d]{1,2})\/([\d]{1,2})\/([\d]{2})/) {
278 0           $yyyy = $3 + 2000;
279             }
280             else {
281 0           print STDERR "No match on date during paring of job ".$job->id." for \"$dt\" from:\n$tryProgress\?\n";
282 0           exit 0;
283             }
284 0           $mm = $1; $dd = $2;
  0            
285              
286 0           $tm =~ /([\d]{1,2}):([\d]{1,2}):([\s\d]{0,2})/;
287 0 0         my $h = $1; my $m = $2; my $s = (($3 eq "") ? 0 : $3);
  0            
  0            
288 0 0 0       if (($AMPM =~ /PM/) && ($h != 12)) {
    0 0        
289 0           $h += 12;
290             }
291             elsif (($AMPM =~ /AM/) && ($h == 12)) {
292 0           $h -= 12;
293             }
294             #print STDERR "$dt and $tm became $s, $m, $h, $d, $mm and $yyyy\n";
295 0           my $now = timelocal($s, $m, $h, $dd, $mm-1, $yyyy);
296              
297             #
298             # Augment $msg string with more pieces as long as uncover embedded quoted commas
299 0   0       while (($msg =~ /\\$/) && ($msg !~ /[^\\]\\\\$/)) {
300 0           $msg .= ",".shift @tryRest;
301             }
302              
303 0 0         if ($msg =~ /connecting/) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
304 0           $job->startConnecting($now);
305             }
306             elsif ($msg =~ /connected/) {
307 0           $job->connected($now);
308             }
309             elsif ($msg =~ /^using ([\S]+)/) {
310             }
311             elsif ($msg =~ /^mounting ([\S]+)/) {
312 0           my $volume = NBU::Media->new($1);
313 0           $job->startMounting($now, $volume);
314             }
315             elsif ($msg =~ /mounted/) {
316             # unfortunately this data stream does not tell us which drive :-(
317 0           $job->mounted($now);
318             }
319             elsif ($msg =~ /positioning ([\S]+) to file ([\S]+)/) {
320 0           my $volume = NBU::Media->new($1);
321 0           my $fileNumber = $2;
322 0 0         if (!defined($job->mount)) {
323 0           $job->startMounting($now, $volume);
324 0           $job->mounted($now);
325             }
326 0           $job->startPositioning($fileNumber, $now);
327             }
328             elsif ($msg =~ /positioned/) {
329 0           $job->positioned($now);
330             }
331             elsif ($msg =~ /begin writing/) {
332 0           $job->startWriting($now);
333             }
334             elsif ($msg =~ /end writing/) {
335 0           $job->doneWriting($now);
336             }
337             elsif ($msg =~ /begin reading/) { }
338             elsif ($msg =~ /end reading/) { }
339             #
340             elsif ($msg =~ /Critical bp/) { }
341             elsif ($msg =~ /Critical vlt/) { }
342             elsif ($msg =~ /Error bp/) { }
343             elsif ($msg =~ /Error vlt/) { }
344             elsif ($msg =~ /Info bp/) { }
345             elsif ($msg =~ /Info vlt/) { }
346             elsif ($msg =~ /Info nbdelete/) { }
347             elsif ($msg =~ /Warning bp/) { }
348             elsif ($msg =~ /Warning vlt/) { }
349             elsif ($msg =~ /begin Catalog/) { }
350             elsif ($msg =~ /begin Eject and Report/) { }
351             elsif ($msg =~ /begin Restore/) { }
352             elsif ($msg =~ /begin Choosing Images/) { }
353             elsif ($msg =~ /begin Duplicating Images/) { }
354             elsif ($msg =~ /begin Duplicate/) { }
355             elsif ($msg =~ /begin Import/) { }
356             elsif ($msg =~ /end Catalog/) { }
357             elsif ($msg =~ /end Eject and Report/) { }
358             elsif ($msg =~ /end Restore/) { }
359             elsif ($msg =~ /end Duplicate/) { }
360             elsif ($msg =~ /end Choosing Images/) { }
361             elsif ($msg =~ /end Duplicating Images/) { }
362             elsif ($msg =~ /end Import/) { }
363             elsif ($msg =~ /images required/) { }
364             elsif ($msg =~ /media/) { }
365             elsif ($msg =~ /started process/) { }
366             elsif ($msg =~ /restarted as job/) { }
367             elsif ($msg =~ /restoring image ([\S]+)/) { }
368             elsif ($msg =~ /restored image ([\S]+) -/) { }
369             #
370             # Additions as of NBU 6.5
371             elsif ($msg =~ /Error nbjm/) { }
372             elsif ($msg =~ /begin operation/) { } # Extra space there on purpose: NB bug?
373             elsif ($msg =~ /end operation/) { } # Extra space there on purpose: NB bug?
374             elsif ($msg =~ /begin operation/) { }
375             elsif ($msg =~ /end operation/) { }
376             elsif ($msg =~ /requesting resource/) { }
377             elsif ($msg =~ /granted resource/) { }
378             elsif ($msg =~ /writing to path/) { }
379             elsif ($msg =~ /ended process ([\d]+) \(([\d]+)\)/) { }
380              
381             elsif ($msg =~ /snapshot client is ([\S]+) - snapshot method is ([\S]+)/) {
382 0           my $clientName = $1;
383 0           my $snapShotMethod = $2;
384             }
385             elsif ($msg =~ /collecting BMR information/) { }
386             elsif ($msg =~ /transferring BMR information to the master server/) { }
387             elsif ($msg =~ /BMR information transfer successful/) { }
388              
389             elsif ($msg =~ /path is /) { }
390             elsif ($msg =~ /estimated ([-]?[\d]+) kbytes needed/) { }
391 0           elsif ($msg =~ /([\d]+) KB written/) { $KBWritten += $1; }
392             elsif ($msg =~ /number of files written/) { }
393             elsif ($msg =~ /waiting for vault session ID lock/) { }
394             elsif ($msg =~ /vault session ID lock acquired/) { }
395             elsif ($msg =~ /vault session ID lock released/) { }
396             elsif ($msg =~ /waiting for vault duplication lock/) { }
397             elsif ($msg =~ /vault duplication lock acquired/) { }
398             elsif ($msg =~ /vault duplication lock released/) { }
399             elsif ($msg =~ /waiting for vault assign slot lock/) { }
400             elsif ($msg =~ /vault assign slot lock acquired/) { }
401             elsif ($msg =~ /vault assign slot lock released/) { }
402             elsif ($msg =~ /vault catalog backup started using policy "(.*)" and schedule "(.*)"/) { }
403             elsif ($msg =~ /waiting for vault assign slot lock/) { }
404             elsif ($msg =~ /vault assign slot lock acquired/) { }
405             elsif ($msg =~ /vault assign slot lock released/) { }
406             elsif ($msg =~ /waiting for vault eject lock/) { }
407             elsif ($msg =~ /vault eject lock acquired/) { }
408             elsif ($msg =~ /vault eject lock released/) { }
409             elsif ($msg =~ /vault job lock released/) { }
410             elsif ($msg =~ /vault duplication started - batch ([\d]+) of ([\d]+) for ([\d]+) images/) { }
411             elsif ($msg =~ /vault duplication batch ([\d]+) of ([\d]+) completed. ([\d]+) o. ([\d]+) images duplicated/) { }
412             elsif ($msg =~ /vault catalog backup skipped/) { }
413             else {
414 0           print "$jobID\:$i\: $msg\n";
415             }
416             }
417 0           my $tryKBytesWritten = $KBytesWritten = shift @tryRest;
418 0           my $tryFilesWritten = $filesWritten = shift @tryRest;
419              
420 0           @rest = @tryRest;
421             }
422             }
423              
424 0 0         if (!defined($job->state)) {
425 0           print STDERR "Job ".$job->id." has no state?\n";
426             }
427 0 0         if ($job->state eq "active") {
    0          
428 0 0         my $lastSize = $job->{SIZE} if (defined($job->{SIZE}));
429 0 0         my $lastElapsed = $job->{ELAPSED} if (defined($job->{ELAPSED}));
430              
431 0           $job->{CURRENTFILE} = $currentFile;
432              
433 0 0         $KBytesWritten = $KBWritten if ($KBWritten > 0);
434              
435 0 0         my $size = $job->{SIZE} = $KBytesWritten if ($KBytesWritten ne "");
436 0 0         $job->{FILECOUNT} = $filesWritten if ($filesWritten ne "");
437              
438 0 0         $job->{OPERATION} = $operation if ($operation ne "");
439 0 0         $job->{ELAPSED} = $elapsed if ($elapsed ne "");
440              
441 0 0 0       if (($job->type eq "Backup") || ($job->type eq "Restore")) {
442 0 0 0       if ((defined($lastSize) && defined($size)) && (defined($lastElapsed) && ($elapsed ne ""))) {
      0        
      0        
443 0           $job->{ISPEED} = ($size - $lastSize) / ($elapsed - $lastElapsed);
444             }
445             }
446             }
447             elsif ($job->state eq "done") {
448 0 0         $job->{SIZE} = $KBytesWritten if ($KBytesWritten ne "");
449 0 0         $job->{FILECOUNT} = $filesWritten if ($filesWritten ne "");
450 0           $job->stop($ended, $status);
451 0 0         $job->{ELAPSED} = $elapsed if ($elapsed ne "");
452             }
453            
454 0           return $job;
455             }
456              
457             sub byID {
458 0     0 0   my $Class = shift;
459              
460 0 0         if (@_) {
461 0           my $id = shift;
462              
463 0 0         return $jobs{$id} if (exists($jobs{$id}));
464             }
465 0           return undef;
466             }
467              
468             #
469             # Returns the last job associated with the argument PID.
470             sub byPID {
471 0     0 0   my $Class = shift;
472 0           my $pid = shift;
473              
474 0 0         if (my $pidArray = $pids{$pid}) {
475 0           my $job = @$pidArray[@$pidArray - 1];
476 0 0         if (!defined($job)) {
477 0           print STDERR "Strange doings with pid $pid\n";
478             }
479 0           return $job;
480             }
481 0           return undef;
482             }
483              
484             #
485             # Some jobs turn out to be worse than useless
486             sub forget {
487 0     0 0   my $self = shift;
488              
489 0 0         if (my $pidArray = $pids{$self->pid}) {
490 0           my @newArray;
491 0           foreach my $job (@$pidArray) {
492 0 0         push @newArray, $job unless ($job eq $self);
493             }
494 0 0         if (@newArray < 0) {
495 0           delete $pids{$self->pid};
496             }
497             else {
498 0           $pids{$self->pid} = \@newArray;
499             }
500             }
501 0 0         if ($self->id) {
502 0           delete $jobs{$self->id};
503             }
504              
505 0           return $self;
506             }
507              
508             sub pid {
509 0     0 0   my $self = shift;
510              
511 0           return $self->{PID};
512             }
513              
514             sub id {
515 0     0 0   my $self = shift;
516              
517 0 0         if (@_) {
518 0           $jobs{$self->{ID} = shift} = $self;
519             }
520 0           return $self->{ID};
521             }
522              
523             sub mediaServer {
524 0     0 0   my $self = shift;
525              
526 0 0         if (@_) {
527 0           $self->{MEDIASERVER} = shift;
528             }
529 0           return $self->{MEDIASERVER};
530             }
531              
532             #
533             # A job's backup ID really identifies the image that job wrote
534             # out to the volume(s).
535             sub backupID {
536 0     0 0   my $self = shift;
537              
538 0 0         if (@_) {
539 0           my $image = NBU::Image->new(shift);
540 0           $self->{IMAGE} = $image;
541             }
542              
543 0           return $self->{IMAGE};
544             }
545              
546             sub image {
547 0     0 0   my $self = shift;
548              
549 0 0         if (@_) {
550 0           my $backupid = shift;
551 0           my $image;
552 0 0 0       if (!defined($image = $self->{IMAGE}) || ($image->id ne $backupid)) {
553 0           $image = NBU::Image->new($backupid);
554 0           $self->{IMAGE} = $image;
555             }
556 0           $self->{IMAGE} = $image;
557             }
558              
559 0           return $self->{IMAGE};
560             }
561              
562             #
563             sub client {
564 0     0 0   my $self = shift;
565              
566 0 0         if (@_) {
567 0           $self->{CLIENT} = shift;
568             }
569              
570 0 0 0       if (!defined($self->{CLIENT}) && defined($self->{IMAGE})) {
571 0           $self->{CLIENT} = $self->image->client;
572             }
573              
574 0           return $self->{CLIENT};
575             }
576              
577             #
578             # written.
579             sub class {
580 0     0 0   my $self = shift;
581              
582 0 0         if (@_) {
583 0           $self->{CLASS} = shift;
584             }
585              
586 0 0 0       if (!defined($self->{CLASS}) && defined($self->{IMAGE})) {
587 0           $self->{CLASS} = $self->image->class;
588             }
589              
590 0           return $self->{CLASS};
591             }
592             sub schedule {
593 0     0 0   my $self = shift;
594              
595 0           return $self->{SCHEDULE};
596             }
597              
598             sub start {
599 0     0 0   my $self = shift;
600              
601 0 0         if (@_) {
602 0           $self->{START} = shift;
603             }
604 0           return $self->{START};
605             }
606              
607             sub stop {
608 0     0 0   my $self = shift;
609              
610 0 0         if (@_) {
611 0           $self->{STOP} = shift;
612 0 0         if ($self->mount) {
613 0           $self->mount->unmount($self->{STOP});
614             }
615 0           $self->{STATUSCODE} = shift;
616 0           $self->{ELAPSED} = undef;
617             }
618 0           return $self->{STOP};
619             }
620              
621             sub status {
622 0     0 0   my $self = shift;
623              
624 0           return $self->{STATUSCODE};
625             }
626              
627             my %successCodes = (
628             0 => 1,
629             1 => 1,
630             );
631             sub success {
632 0     0 0   my $self = shift;
633              
634 0 0         return 1 if ($self->state ne "done");
635 0           return exists($successCodes{$self->{STATUSCODE}});
636             }
637              
638             sub errors {
639 0     0 0   my $self = shift;
640 0           my @errorList;
641              
642 0           my $window = "";
643 0 0         $window .= "-d ".NBU->date($self->start)
644             ." -e ".NBU->date($self->stop + 60)
645             if (NBU->me->NBUVersion ne "3.2.0");
646 0 0 0       unless (!defined($self->status) || !$self->status) {
647 0           my $pipe = NBU->cmd("bperror -jobid ".$self->id." $window -problems");
648 0           while (<$pipe>) {
649 0           chop;
650 0           my ($tm, $version, $type, $severity, $serverName, $jobID, $jobGroupID, $u, $clientName, $who, $msg) =
651             split(/[\s]+/, $_, 11);
652 0           $msg =~ s/from client ${clientName}: (WRN|ERR|INF) - //;
653 0           my %e = (
654             tod => $tm,
655             severity => $severity,
656             who => $who,
657             message => $msg,
658             );
659 0           push @errorList, \%e;
660             }
661             }
662 0           return (@errorList);
663             }
664              
665             sub elapsedTime {
666 0     0 0   my $self = shift;
667              
668 0 0         if ($self->{ELAPSED}) {
669 0           return $self->{ELAPSED};
670             }
671             else {
672 0           my $stop = $self->stop;
673 0 0         $stop = time() if (!$stop);
674              
675 0           return ($stop - $self->start);
676             }
677             }
678              
679             sub storageUnit {
680 0     0 0   my $self = shift;
681              
682 0 0         if (@_) {
683 0           $self->{STUNIT} = shift;
684             }
685 0           return $self->{STUNIT};
686             }
687              
688             sub mountList {
689 0     0 0   my $self = shift;
690 0           my $ml = $self->{MOUNTLIST};
691              
692 0           return %$ml;
693             }
694              
695             sub pushState {
696 0     0 0   my $self = shift;
697 0           my $newState = shift;
698 0           my $tm = shift;
699              
700 0           my $states = $self->{STATES};
701 0           my $times = $self->{TIMES};
702 0 0         if (!$states) {
    0          
703 0           $states = $self->{STATES} = [];
704 0           $times = $self->{TIMES} = [];
705             }
706             #
707             # If the current state is the same as the last state we replace
708             # it rather than layering it.
709             elsif (defined(my $lastState = pop @$states)) {
710 0 0         if ($lastState eq $newState) {
711 0           $tm = pop @$times;
712             }
713             else {
714 0           push @$states, $lastState;
715             }
716             }
717 0           push @$states, $newState;
718 0           push @$times, $tm;
719 0           $self->{STARTOP} = $tm;
720             }
721              
722             sub popState {
723 0     0 0   my $self = shift;
724 0           my $tm = shift;
725 0           my $states = $self->{STATES};
726 0           my $times = $self->{TIMES};
727              
728 0 0         if (defined(my $lastState = pop @$states)) {
729 0           $self->{$lastState} += ($tm - $self->{STARTOP});
730 0           $self->{STARTOP} = pop @$times;
731             }
732             }
733              
734             sub volume {
735 0     0 0   my $self = shift;
736              
737 0           return $self->{SELECTED};
738             }
739              
740             sub startConnecting {
741 0     0 0   my $self = shift;
742 0           my $tm = shift;
743              
744 0           $self->pushState('CON', $tm);
745             }
746              
747             sub connected {
748 0     0 0   my $self = shift;
749 0           my $tm = shift;
750              
751 0           $self->popState($tm);
752             }
753              
754             sub fileOpened {
755 0     0 0   my $self = shift;
756 0           my $tm = shift;
757 0           my $file = shift;
758              
759              
760 0           my $fileListR = $self->{FILELIST};
761 0           $$fileListR{$tm} = $file;
762              
763 0           my $mount = NBU::Mount->new($self, $file, $self->storageUnit->path, $tm);
764              
765 0           my $mountListR = $self->{MOUNTLIST};
766 0           $$mountListR{$tm} = $mount;
767              
768 0           return $self->mount($mount);
769             }
770              
771              
772             sub startMounting {
773 0     0 0   my $self = shift;
774 0           my $tm = shift;
775 0           my $volume = shift;
776              
777 0           $self->pushState('MNT', $tm);
778              
779 0           $self->{SELECTED} = $volume;
780 0           $volume->selected($tm);
781              
782 0           return $self;
783             }
784              
785             sub mounted {
786 0     0 0   my $self = shift;
787 0           my $tm = shift;
788 0           my $drive = shift;
789              
790 0 0         if (defined(my $volume = $self->{SELECTED})) {
791 0           $self->popState($tm);
792              
793 0           my $mount = NBU::Mount->new($self, $volume, $drive, $tm);
794              
795 0           my $mountListR = $self->{MOUNTLIST};
796 0           $$mountListR{$tm} = $mount;
797              
798 0           return $self->mount($mount);
799             }
800             else {
801 0           return undef;
802             }
803             }
804              
805             sub startPositioning {
806 0     0 0   my $self = shift;
807 0           my $fileNumber = shift;
808 0           my $tm = shift;
809              
810 0           my $mount = $self->mount;
811 0 0         if (defined($mount)) {
812 0           $self->pushState('POS', $tm);
813             # $self->mount->startPositioning($fileNumber, $tm);
814             }
815 0           return $mount;
816             }
817              
818             sub positioned {
819 0     0 0   my $self = shift;
820 0           my $tm = shift;
821              
822 0           my $mount = $self->mount;
823 0 0         if (defined($mount)) {
824 0           $self->popState($tm);
825             # $self->mount->positioned($tm);
826             }
827 0           return $mount;
828             }
829              
830             sub startWriting {
831 0     0 0   my $self = shift;
832 0           my $tm = shift;
833              
834 0           $self->pushState('WRI', $tm);
835 0           $self->{FRAGMENTCOUNTER}++;
836             }
837              
838             sub doneWriting {
839 0     0 0   my $self = shift;
840 0           my $tm = shift;
841              
842 0           $self->popState($tm);
843             }
844              
845             sub type {
846 0     0 0   my $self = shift;
847              
848 0 0         if (@_) {
849 0           $self->{TYPE} = shift;
850             }
851              
852 0           return $self->{TYPE};
853             }
854              
855             my @jobStates = ("queued", "active", "re-queued", "done", undef, "incomplete");
856             sub state {
857 0     0 0   my $self = shift;
858              
859 0 0         if (@_) {
860 0           $self->{STATE} = shift;
861             }
862 0           return $jobStates[$self->{STATE}];
863             }
864              
865             sub active {
866 0     0 0   my $self = shift;
867              
868 0           return ($self->{STATE} == 1);
869             }
870              
871             sub done {
872 0     0 0   my $self = shift;
873              
874 0   0       return (($self->{STATE} == 3) || ($self->{STATE} == 5));
875             }
876              
877             sub queued {
878 0     0 0   my $self = shift;
879              
880 0   0       return (($self->{STATE} == 0) || ($self->{STATE} == 2));
881             }
882              
883             sub busy {
884 0     0 0   my $self = shift;
885              
886 0 0         if (!$self->{STARTOP}) {
887             #print STDERR "Job ".$self->id." has no start op?\n";
888 0           return undef;
889             }
890             else {
891 0           return $asOf - $self->{STARTOP};
892             }
893             }
894              
895             sub files {
896 0     0 0   my $self = shift;
897              
898 0 0         if (defined(my $fileListR = $self->{FILES})) {
899 0           return@$fileListR;
900             }
901             else {
902 0           return ();
903             }
904             }
905              
906             my %opCodes = (
907             -1 => '---',
908             25 => 'WAI',
909             2 => 'CON',
910             26 => 'CON',
911             0 => ' ? ',
912             27 => 'MNT',
913             29 => 'POS',
914             3 => 'WRI',
915             35 => 'WRI',
916             );
917              
918             sub operation {
919 0     0 0   my $self = shift;
920              
921 0 0         return undef if ($self->state ne "active");
922              
923 0 0         if (@_) {
924 0           my $opCode = shift;
925 0 0         $self->{OPERATION} = $opCode unless ($opCode == 0);
926             }
927 0           my $opCode;
928 0 0         if (!defined($self->{OPERATION})) {
    0          
929 0           return "---";
930             }
931             elsif (!defined($opCode = $opCodes{$self->{OPERATION}})) {
932 0           $opCode = sprintf("%3d", $self->{OPERATION});
933             }
934              
935 0           return $opCode;
936             }
937              
938             sub currentFile {
939 0     0 0   my $self = shift;
940              
941 0 0         return ($self->state eq "active") ? $self->{CURRENTFILE} : undef;
942             }
943              
944             sub try {
945 0     0 0   my $self = shift;
946              
947 0 0         if (@_) {
948 0           my $try = shift;
949              
950 0 0 0       if ($self->{TRY} && ($self->{TRY} != $try)) {
951             # Maybe call somebody that we're on our next try?
952             }
953 0           $self->{TRY} = $try;
954             }
955              
956 0           return $self->{TRY};
957             }
958              
959             sub mount {
960 0     0 0   my $self = shift;
961              
962 0 0         if (@_) {
963 0           my $newMount = shift;
964 0 0         if (defined(my $currentMount = $self->{MOUNT})) {
965 0           $self->{MOUNT} = undef;
966 0 0         if (defined($newMount)) {
967 0           $currentMount->unmount($newMount->start);
968             }
969             else {
970 0 0         print STDERR "unmounting without unmount time!\n" if (!$currentMount->stop);
971             }
972             }
973 0           $self->{MOUNT} = $newMount;
974             }
975 0           return $self->{MOUNT};
976             }
977              
978             sub read {
979 0     0 0   my $self = shift;
980 0           my ($fragment, $size, $speed) = @_;
981              
982 0           $self->{SIZE} += $size;
983 0           $self->mount->read($fragment, $size, $speed);
984              
985 0           return $self;
986             }
987              
988             sub write {
989 0     0 0   my $self = shift;
990 0           my ($fragment, $size, $speed) = @_;
991              
992 0           $self->{SIZE} += $size;
993 0           $self->mount->write($fragment, $size, $speed);
994              
995 0           return $self;
996             }
997              
998             sub networkReadStats {
999 0     0 0   my $self = shift;
1000              
1001 0 0         if (@_) {
1002 0           my ($noBuffer, $count) = @_;
1003              
1004 0           $self->{NOEMPTYBUFFER} = $noBuffer;
1005 0           $self->{NOEMPTYDELAY} = $count;
1006             }
1007              
1008 0           return ($self->{NOEMPTYBUFFER}, $self->{NOEMPTYDELAY},
1009             $self->{NOEMPTYDELAY} * $self->{EMPTYWAIT});
1010             }
1011              
1012             sub driveWriteStats {
1013 0     0 0   my $self = shift;
1014              
1015 0 0         if (@_) {
1016 0           my ($noBuffer, $count) = @_;
1017              
1018 0           $self->{NOFULLBUFFER} = $noBuffer;
1019 0           $self->{NOFULLDELAY} = $count;
1020             }
1021              
1022 0           return ($self->{NOFULLBUFFER}, $self->{NOFULLDELAY},
1023             $self->{NOFULLDELAY} * $self->{FULLWAIT});
1024             }
1025              
1026             sub dataBufferSize {
1027 0     0 0   my $self = shift;
1028              
1029 0 0         if (@_) {
1030 0           my ($size) = @_;
1031              
1032 0           $self->{BUFFERSIZE} = $size;
1033             }
1034              
1035 0           return ($self->{BUFFERSIZE});
1036             }
1037              
1038             sub dataBufferCount {
1039 0     0 0   my $self = shift;
1040              
1041 0 0         if (@_) {
1042 0           my ($count) = @_;
1043              
1044 0           $self->{BUFFERCOUNT} = $count;
1045             }
1046              
1047 0           return ($self->{BUFFERCOUNT});
1048             }
1049              
1050             sub delayCycles {
1051 0     0 0   my $self = shift;
1052              
1053 0 0         if (@_) {
1054 0           my ($emptyWaitTime, $fullWaitTime) = @_;
1055              
1056 0           $self->{EMPTYWAIT} = $emptyWaitTime / 1000;
1057 0           $self->{FULLWAIT} = $fullWaitTime / 1000;
1058             }
1059              
1060 0           return ($self->{EMPTYWAIT}, $self->{FULLWAIT});
1061             }
1062              
1063             sub dataWritten {
1064 0     0 0   my $self = shift;
1065              
1066 0           return $self->{SIZE};
1067             }
1068              
1069             sub speed {
1070 0     0 0   my $self = shift;
1071              
1072 0 0         if ($self->success) {
1073 0           return ($self->dataWritten / $self->elapsedTime);
1074             }
1075 0           return undef;
1076             }
1077              
1078             sub ispeed {
1079 0     0 0   my $self = shift;
1080              
1081 0 0         return $self->speed if ($self->state eq "done");
1082 0           return $self->{ISPEED};
1083             }
1084              
1085             sub filesWritten {
1086 0     0 0   my $self = shift;
1087              
1088 0           return $self->{FILECOUNT};
1089             }
1090              
1091             sub printHeader {
1092 0     0 0   my $self = shift;
1093              
1094 0           my $pid = $self->pid;
1095 0           my $id = $self->id;
1096 0           print "Process $pid manages job $id\n";
1097              
1098 0           return $self;
1099             }
1100              
1101             #
1102             # The business and system methods are here to facilitate job reporting
1103             # that is business system oriented rather than a simple backup stream
1104             # accounting line-item listing
1105             sub business {
1106 0     0 0   my $self = shift;
1107              
1108 0 0         if (@_) {
1109 0           $self->{BUSINESS} = shift;
1110             }
1111 0           return $self->{BUSINESS};
1112             }
1113              
1114             sub system {
1115 0     0 0   my $self = shift;
1116              
1117 0 0         if (@_) {
1118 0           $self->{SYSTEM} = shift;
1119             }
1120 0           return $self->{SYSTEM};
1121             }
1122              
1123             1;
1124              
1125             __END__