1 # Copyright (c) 2010 Zmanda, Inc. All Rights Reserved.
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.
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
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
16 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19 package Amanda::Taper::Scan::lexical;
23 Amanda::Taper::Scan::lexical
27 This package implements the "lexical" taperscan algorithm. See
28 C<amanda-taperscan(7)>.
34 use base qw( Amanda::ScanInventory Amanda::Taper::Scan );
40 use base qw(Exporter);
41 our @EXPORT_OK = qw($DEFAULT_CHANGER);
45 use Amanda::Device qw( :constants );
46 use Amanda::Debug qw( debug );
49 use Amanda::Interactivity;
50 use Amanda::Taper::Scan::traditional;
52 our $DEFAULT_CHANGER = {};
58 my $chg = $params{'changer'};
60 $chg = Amanda::Changer->new();
61 $params{'changer'} = $chg;
63 if (!$chg->have_inventory()) {
64 return Amanda::Taper::Scan::traditional->new(%params);
66 my $self = Amanda::ScanInventory->new(%params);
67 return bless ($self, $class);
73 my $last_label = $self->last_use_label();
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'});
80 $result = $tle if $self->is_reusable_volume(label => $tle->{'label'}) and
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'}));
92 $result = $same_label if !defined $result;
93 return $result->{'label'} if $result;
97 sub first_reusable_label {
102 for my $tle (@{$self->{'tapelist'}->{'tles'}}) {
103 $label = $tle->{'label'} if $self->is_reusable_volume(label => $tle->{'label'});
111 my $tles = $self->{'tapelist'}->{tles};
112 my $label = $tles->[0]->{'label'};
117 my $inventory = shift;
124 my $first_new_volume;
130 my $label = $self->most_prefered();
131 $self->{'most_prefered_label'} = $label;
133 for my $i (0..(scalar(@$inventory)-1)) {
134 my $sl = $inventory->[$i];
135 if ($sl->{current}) {
138 next if $seen->{$sl->{slot}} and (!$res || $res->{'this_slot'} ne $sl->{'slot'});
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'})) {
151 my $vol_tle = $self->{'tapelist'}->lookup_tapelabel($sl->{'label'});
153 if ($vol_tle->{'datestamp'} eq '0') {
154 push @new_labeled, $sl;
156 } elsif ($self->{'chg'}->volume_is_labelable($sl->{'device_status'},
159 $first_new_volume = $sl if !$first_new_volume;
160 $new_volume = $sl if $current && !$new_volume;
161 push @new_volume, $sl;
164 } elsif ($self->{'chg'}->volume_is_labelable($sl->{'device_status'},
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;
176 $unknown = $first_unknown if !defined $unknown;
177 $new_volume = $first_new_volume if !defined $new_volume;
179 my $first_label = $self->first_reusable_label();
180 my $last_label = $self->last_use_label();
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'});
194 for my $sl (@new_labeled) {
195 $new_labeled = $sl if !defined $new_labeled or
197 $sl->{'label'} lt $new_labeled->{'label'}) or
199 $sl->{'label'} gt $last_label and
200 $sl->{'label'} lt $new_labeled->{'label'}) or
202 $sl->{'label'} gt $last_label and
203 $new_labeled->{'label'} lt $last_label) or
205 $new_labeled->{'label'} lt $last_label and
206 $sl->{'label'} lt $new_labeled->{'label'});
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'}));
224 if ($new_labeled && $self->{'scan_conf'}->{'new_labeled'} eq 'soon') {
226 } elsif ($new_volume && $self->{'scan_conf'}->{'new_volume'} eq 'soon') {
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'}))) {
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'}))) {
254 } elsif (defined $most_prefered) {
255 $use = $most_prefered;
256 } elsif (defined $reusable) {
258 } elsif ($new_labeled and $self->{'scan_conf'}->{'new_labeled'} eq 'last') {
260 } elsif ($new_volume and $self->{'scan_conf'}->{'new_volume'} eq 'last') {
265 if (defined $res and $res->{'this_slot'} eq $use->{'slot'}) {
266 return (Amanda::ScanInventory::SCAN_DONE);
268 return (Amanda::ScanInventory::SCAN_LOAD,
271 } elsif ($unknown and $self->{'scan_conf'}->{'scan'}) {
272 return (Amanda::ScanInventory::SCAN_LOAD,
274 } elsif ($self->{'scan_conf'}->{'ask'}) {
275 return (Amanda::ScanInventory::SCAN_ASK_POLL);
277 return (Amanda::ScanInventory::SCAN_FAIL);