1 # Copyright (c) 2010-2012 Zmanda, Inc. All Rights Reserved.
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.
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
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
17 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20 package Amanda::Taper::Scan::lexical;
24 Amanda::Taper::Scan::lexical
28 This package implements the "lexical" taperscan algorithm. See
29 C<amanda-taperscan(7)>.
35 use base qw( Amanda::ScanInventory Amanda::Taper::Scan );
41 use base qw(Exporter);
42 our @EXPORT_OK = qw($DEFAULT_CHANGER);
46 use Amanda::Device qw( :constants );
47 use Amanda::Debug qw( debug );
50 use Amanda::Interactivity;
51 use Amanda::Taper::Scan::traditional;
53 our $DEFAULT_CHANGER = {};
59 my $chg = $params{'changer'};
61 $chg = Amanda::Changer->new();
62 $params{'changer'} = $chg;
64 if (!$chg->have_inventory()) {
65 return Amanda::Taper::Scan::traditional->new(%params);
67 my $self = Amanda::ScanInventory->new(%params);
68 return bless ($self, $class);
74 my $last_label = $self->last_use_label();
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'});
81 $result = $tle if $self->is_reusable_volume(label => $tle->{'label'}) and
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'}));
93 $result = $same_label if !defined $result;
94 return $result->{'label'} if $result;
98 sub first_reusable_label {
103 for my $tle (@{$self->{'tapelist'}->{'tles'}}) {
104 $label = $tle->{'label'} if $self->is_reusable_volume(label => $tle->{'label'});
112 my $tles = $self->{'tapelist'}->{tles};
113 my $label = $tles->[0]->{'label'};
118 my $inventory = shift;
125 my $first_new_volume;
131 my $label = $self->most_prefered();
132 $self->{'most_prefered_label'} = $label;
134 for my $i (0..(scalar(@$inventory)-1)) {
135 my $sl = $inventory->[$i];
136 if ($sl->{current}) {
139 next if $seen->{$sl->{slot}} and (!$res || $res->{'this_slot'} ne $sl->{'slot'});
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'})) {
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;
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;
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;
181 $unknown = $first_unknown if !defined $unknown;
182 $new_volume = $first_new_volume if !defined $new_volume;
184 my $first_label = $self->first_reusable_label();
185 my $last_label = $self->last_use_label();
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'});
199 for my $sl (@new_labeled) {
200 $new_labeled = $sl if !defined $new_labeled or
202 $sl->{'label'} lt $new_labeled->{'label'}) or
204 $sl->{'label'} gt $last_label and
205 $sl->{'label'} lt $new_labeled->{'label'}) or
207 $sl->{'label'} gt $last_label and
208 $new_labeled->{'label'} lt $last_label) or
210 $new_labeled->{'label'} lt $last_label and
211 $sl->{'label'} lt $new_labeled->{'label'});
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'}));
226 if ($new_labeled && $self->{'scan_conf'}->{'new_labeled'} eq 'soon') {
228 } elsif ($new_volume && $self->{'scan_conf'}->{'new_volume'} eq 'soon') {
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'}))) {
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'}))) {
256 } elsif (defined $most_prefered) {
257 $use = $most_prefered;
258 } elsif (defined $reusable) {
260 } elsif ($new_labeled and $self->{'scan_conf'}->{'new_labeled'} eq 'last') {
262 } elsif ($new_volume and $self->{'scan_conf'}->{'new_volume'} eq 'last') {
267 if (defined $res and $res->{'this_slot'} eq $use->{'slot'}) {
268 return (Amanda::ScanInventory::SCAN_DONE);
270 return (Amanda::ScanInventory::SCAN_LOAD,
273 } elsif ($unknown and $self->{'scan_conf'}->{'scan'}) {
274 return (Amanda::ScanInventory::SCAN_LOAD,
276 } elsif ($self->{'scan_conf'}->{'ask'}) {
277 return (Amanda::ScanInventory::SCAN_ASK_POLL);
279 return (Amanda::ScanInventory::SCAN_FAIL);