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