Imported Upstream version 3.3.0
[debian/amanda] / perl / Amanda / Taper / Scan / lexical.pm
1 # Copyright (c) 2010 Zmanda, Inc.  All Rights Reserved.
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10 # for more details.
11 #
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 #
16 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
18
19 package Amanda::Taper::Scan::lexical;
20
21 =head1 NAME
22
23 Amanda::Taper::Scan::lexical
24
25 =head1 SYNOPSIS
26
27 This package implements the "lexical" taperscan algorithm.  See
28 C<amanda-taperscan(7)>.
29
30 =cut
31
32 use strict;
33 use warnings;
34 use base qw( Amanda::ScanInventory Amanda::Taper::Scan );
35 use Amanda::Tapelist;
36 use Carp;
37 use POSIX ();
38 use Data::Dumper;
39 use vars qw( @ISA );
40 use base qw(Exporter);
41 our @EXPORT_OK = qw($DEFAULT_CHANGER);
42
43 use Amanda::Paths;
44 use Amanda::Util;
45 use Amanda::Device qw( :constants );
46 use Amanda::Debug qw( debug );
47 use Amanda::Changer;
48 use Amanda::MainLoop;
49 use Amanda::Interactivity;
50 use Amanda::Taper::Scan::traditional;
51
52 our $DEFAULT_CHANGER = {};
53
54 sub new {
55     my $class = shift;
56     my %params = @_;
57
58     my $chg = $params{'changer'};
59     if (!defined $chg) {
60         $chg = Amanda::Changer->new();
61         $params{'changer'} = $chg;
62     }
63     if (!$chg->have_inventory()) {
64         return Amanda::Taper::Scan::traditional->new(%params);
65     }
66     my $self = Amanda::ScanInventory->new(%params);
67     return bless ($self, $class);
68 }
69
70 sub most_prefered {
71     my $self = shift;
72
73     my $last_label = $self->last_use_label();
74     my $same_label;
75     my $result;
76     for my $tle (@{$self->{'tapelist'}->{'tles'}}) {
77         if (defined $last_label && $last_label eq $tle->{'label'}) {
78             $same_label = $tle if $self->is_reusable_volume(label => $tle->{'label'});
79         } else {
80             $result = $tle if $self->is_reusable_volume(label => $tle->{'label'}) and
81                               (!defined $result or
82                                (!defined $last_label and
83                                 $tle->{'label'} lt $result->{'label'}) or
84                                ($tle->{'label'} gt $last_label and
85                                 $tle->{'label'} lt $result->{'label'}) or
86                                ($tle->{'label'} gt $last_label and
87                                 $result->{'label'} lt $last_label) or
88                                ($result->{'label'} lt $last_label and
89                                 $tle->{'label'} lt $result->{'label'}));
90         }
91     }
92     $result = $same_label if !defined $result;
93     return $result->{'label'} if $result;
94     return undef;
95 }
96
97 sub first_reusable_label {
98     my $self = shift;
99
100     my $label;
101
102     for my $tle (@{$self->{'tapelist'}->{'tles'}}) {
103         $label = $tle->{'label'} if $self->is_reusable_volume(label => $tle->{'label'});
104     }
105     return $label;
106 }
107
108 sub last_use_label {
109     my $self = shift;
110
111     my $tles = $self->{'tapelist'}->{tles};
112     my $label = $tles->[0]->{'label'};
113 }
114
115 sub analyze {
116     my $self = shift;
117     my $inventory  = shift;
118     my $seen  = shift;
119     my $res = shift;
120
121     my $most_prefered;
122     my @reusable;
123     my @new_labeled;
124     my $first_new_volume;
125     my $new_volume;
126     my @new_volume;
127     my $first_unknown;
128     my $unknown;
129     my $current;
130     my $label = $self->most_prefered();
131     $self->{'most_prefered_label'} = $label;
132
133     for my $i (0..(scalar(@$inventory)-1)) {
134         my $sl = $inventory->[$i];
135         if ($sl->{current}) {
136             $current = $sl;
137         }
138         next if $seen->{$sl->{slot}} and (!$res || $res->{'this_slot'} ne $sl->{'slot'});
139
140         if (!defined $sl->{'state'} ||
141             $sl->{'state'} == Amanda::Changer::SLOT_UNKNOWN) {
142             $first_unknown = $sl if !$first_unknown;
143             $unknown = $sl if $current && !$unknown;
144         } elsif ($sl->{'state'} == Amanda::Changer::SLOT_EMPTY) {
145         } elsif (defined $sl->{'label'}) {
146             if ($label && $sl->{'label'} eq $label) {
147                 $most_prefered = $sl;
148             } elsif ($self->is_reusable_volume(label => $sl->{'label'})) {
149                 push @reusable, $sl;
150             } else {
151                 my $vol_tle = $self->{'tapelist'}->lookup_tapelabel($sl->{'label'});
152                 if ($vol_tle) {
153                     if ($vol_tle->{'datestamp'} eq '0') {
154                         push @new_labeled, $sl;
155                     }
156                 } elsif ($self->{'chg'}->volume_is_labelable($sl->{'device_status'},
157                                                              $sl->{'f_type'},
158                                                              $sl->{'label'})) {
159                     $first_new_volume = $sl if !$first_new_volume;
160                     $new_volume = $sl if $current && !$new_volume;
161                     push @new_volume, $sl;
162                 }
163             }
164         } elsif ($self->{'chg'}->volume_is_labelable($sl->{'device_status'},
165                                                      $sl->{'f_type'},
166                                                      $sl->{'label'})) {
167             $first_new_volume = $sl if !$first_new_volume;
168             $new_volume = $sl if $current && !$new_volume;
169             push @new_volume, $sl;
170         } elsif (!defined($sl->{device_status}) && !defined($sl->{label})) {
171             $first_unknown = $sl if !$first_unknown;
172             $unknown = $sl if $current && !$unknown;
173         } else {
174         }
175     }
176     $unknown = $first_unknown if !defined $unknown;
177     $new_volume = $first_new_volume if !defined $new_volume;
178
179     my $first_label = $self->first_reusable_label();
180     my $last_label = $self->last_use_label();
181
182     my $reusable;
183     for my $sl (@reusable) {
184         $reusable = $sl if !defined $reusable or
185                            ($sl->{'label'} gt $last_label and
186                             $sl->{'label'} lt $reusable->{'label'}) or
187                            ($sl->{'label'} gt $last_label and
188                             $reusable->{'label'} lt $last_label) or
189                            ($reusable->{'label'} lt $last_label and
190                             $sl->{'label'} lt $reusable->{'label'});
191     }
192
193     my $new_labeled;
194     for my $sl (@new_labeled) {
195         $new_labeled = $sl if !defined $new_labeled or
196                               (!$last_label and
197                                $sl->{'label'} lt $new_labeled->{'label'}) or
198                               ($last_label and
199                                $sl->{'label'} gt $last_label and
200                                $sl->{'label'} lt $new_labeled->{'label'}) or
201                               ($last_label and
202                                $sl->{'label'} gt $last_label and
203                                $new_labeled->{'label'} lt $last_label) or
204                               ($last_label and
205                                $new_labeled->{'label'} lt $last_label and
206                                $sl->{'label'} lt $new_labeled->{'label'});
207     }
208
209     for my $sl (@new_volume) {
210         $sl->{'label'} = $self->{'chg'}->make_new_tape_label(
211                                         barcode => $sl->{'barcode'},
212                                         meta => $sl->{'meta'});
213         $new_volume = $sl if defined $last_label and
214                              $new_volume->{'label'} ne $sl->{'label'} and
215                              (($sl->{'label'} gt $last_label and
216                                $sl->{'label'} lt $new_volume->{'label'}) or
217                               ($sl->{'label'} gt $last_label and
218                                $new_volume->{'label'} lt $last_label) or
219                               ($new_volume->{'label'} lt $last_label and
220                                $sl->{'label'} lt $new_volume->{'label'}));
221     }
222
223     my $use;
224     if ($new_labeled && $self->{'scan_conf'}->{'new_labeled'} eq 'soon') {
225         $use = $new_labeled;
226     } elsif ($new_volume && $self->{'scan_conf'}->{'new_volume'} eq 'soon') {
227         $use = $new_volume;
228     } elsif ($new_labeled &&
229               $self->{'scan_conf'}->{'new_labeled'} eq 'order' and
230               (!$label || !$first_label || !$last_label || !$most_prefered or
231                ($last_label and $most_prefered and
232                 $new_labeled->{'label'} gt $last_label and
233                 $new_labeled->{'label'} lt $most_prefered->{'label'}) or
234                ($last_label and $most_prefered and
235                 $new_labeled->{'label'} gt $last_label and
236                 $most_prefered->{'label'} lt $last_label) or
237                ($first_label and $most_prefered and
238                 $new_labeled->{'label'} lt $first_label and
239                 $new_labeled->{'label'} lt $most_prefered->{'label'}))) {
240         $use = $new_labeled;
241     } elsif ($new_volume and
242              $self->{'scan_conf'}->{'new_volume'} eq 'order' and
243              (!$label || !$first_label || !$last_label || !$most_prefered or
244               ($last_label and $most_prefered and
245                $new_volume->{'label'} gt $last_label and
246                $new_volume->{'label'} lt $most_prefered->{'label'}) or
247               ($last_label and $most_prefered and
248                $new_volume->{'label'} gt $last_label and
249                $most_prefered->{'label'} lt $last_label) or
250               ($first_label and $most_prefered and
251                $new_volume->{'label'} lt $first_label and
252                $new_volume->{'label'} lt $most_prefered->{'label'}))) {
253         $use = $new_volume;
254     } elsif (defined $most_prefered) {
255         $use = $most_prefered;
256     } elsif (defined $reusable) {
257         $use = $reusable;
258     } elsif ($new_labeled and $self->{'scan_conf'}->{'new_labeled'} eq 'last') {
259         $use = $new_labeled;
260     } elsif ($new_volume and $self->{'scan_conf'}->{'new_volume'} eq 'last') {
261         $use = $new_volume;
262     }
263
264     if ($use) {
265         if (defined $res and $res->{'this_slot'} eq $use->{'slot'}) {
266             return (Amanda::ScanInventory::SCAN_DONE);
267         } else {
268             return (Amanda::ScanInventory::SCAN_LOAD,
269                     $use->{'slot'});
270         }
271     } elsif ($unknown and $self->{'scan_conf'}->{'scan'}) {
272         return (Amanda::ScanInventory::SCAN_LOAD,
273                 $unknown->{'slot'});
274     } elsif ($self->{'scan_conf'}->{'ask'}) {
275         return (Amanda::ScanInventory::SCAN_ASK_POLL);
276     } else {
277         return (Amanda::ScanInventory::SCAN_FAIL);
278     }
279 }
280
281 1;