# Copyright (c) 2010-2012 Zmanda, Inc. All Rights Reserved.
# Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Contact information: Carbonite Inc., 756 N Pastoria Ave
# Sunnyvale, CA 94085, or: http://www.zmanda.com
package Amanda::Taper::Scan::lexical;
=head1 NAME
Amanda::Taper::Scan::lexical
=head1 SYNOPSIS
This package implements the "lexical" taperscan algorithm. See
C<amanda-taperscan(7)>.
=cut
use strict;
use warnings;
use base qw( Amanda::ScanInventory Amanda::Taper::Scan );
use Amanda::Tapelist;
use Carp;
use POSIX ();
use Data::Dumper;
use vars qw( @ISA );
use base qw(Exporter);
our @EXPORT_OK = qw($DEFAULT_CHANGER);
use Amanda::Paths;
use Amanda::Util qw( match_labelstr );
use Amanda::Device qw( :constants );
use Amanda::Debug qw( debug );
use Amanda::Changer;
use Amanda::MainLoop;
use Amanda::Interactivity;
use Amanda::Taper::Scan::traditional;
our $DEFAULT_CHANGER = {};
sub new {
my $class = shift;
my %params = @_;
my $chg = $params{'changer'};
if (!defined $chg) {
$chg = Amanda::Changer->new();
$params{'changer'} = $chg;
}
if (!$chg->have_inventory()) {
return Amanda::Taper::Scan::traditional->new(%params);
}
my $self = Amanda::ScanInventory->new(%params);
$self->{'handled-error'} = {};
return bless ($self, $class);
}
sub last_use_label {
my $self = shift;
my $tles = $self->{'tapelist'}->{tles};
return undef if !defined $tles->[0];
my $label = $tles->[0]->{'label'};
}
sub analyze {
my $self = shift;
my $inventory = shift;
my $seen = shift;
my $res = shift;
my @reusable_before;
my @reusable_after;
my @new_volume_before;
my @new_volume_after;
my @new_labeled_before;
my @new_labeled_after;
my @unknown;
my @error;
my $last_label = $self->last_use_label();
for my $i (0..(scalar(@$inventory)-1)) {
my $sl = $inventory->[$i];
next if $seen->{$sl->{slot}};
if (!defined $sl->{'state'} ||
$sl->{'state'} == Amanda::Changer::SLOT_UNKNOWN) {
push @unknown, $sl
} elsif ($sl->{'state'} == Amanda::Changer::SLOT_EMPTY) {
} elsif (defined $sl->{'label'} &&
$sl->{device_status} == $DEVICE_STATUS_SUCCESS) {
if ($self->is_reusable_volume(label => $sl->{'label'})) {
if ($last_label && $sl->{'label'} gt $last_label) {
push @reusable_after, $sl;
} else {
push @reusable_before, $sl;
}
} else {
my $vol_tle = $self->{'tapelist'}->lookup_tapelabel($sl->{'label'});
if ($vol_tle) {
if ($self->volume_is_new_labelled($vol_tle, $sl)) {
if ($last_label && $sl->{'label'} gt $last_label) {
push @new_labeled_after, $sl;
} else {
push @new_labeled_before, $sl;
}
}
} elsif ($self->volume_is_labelable($sl)) {
$sl->{'label'} = $self->{'chg'}->make_new_tape_label(
barcode => $sl->{'barcode'},
slot => $sl->{'slot'},
meta => $sl->{'meta'});
if ($last_label && $sl->{'label'} gt $last_label) {
push @new_volume_after, $sl;
} else {
push @new_volume_before, $sl;
}
}
}
} elsif ($self->volume_is_labelable($sl)) {
$sl->{'label'} = $self->{'chg'}->make_new_tape_label(
barcode => $sl->{'barcode'},
slot => $sl->{'slot'},
meta => $sl->{'meta'});
if ($last_label && $sl->{'label'} gt $last_label) {
push @new_volume_after, $sl;
} else {
push @new_volume_before, $sl;
}
} elsif (!defined($sl->{device_status}) && !defined($sl->{label})) {
push @unknown, $sl;
} elsif (defined($sl->{device_status}) and
($sl->{'device_status'} & $DEVICE_STATUS_DEVICE_ERROR or
$sl->{'device_status'} & $DEVICE_STATUS_VOLUME_ERROR) and
not exists $self->{'handled-error'}->{$sl->{'device_error'}} and
not exists $self->{'new_error'}->{$sl->{'device_error'}}) {
push @error, $sl;
} else {
}
}
@reusable_before = sort { $a->{'label'} cmp $b->{'label'} } @reusable_before;
@reusable_after = sort { $a->{'label'} cmp $b->{'label'} } @reusable_after;
@new_labeled_before = sort { $a->{'label'} cmp $b->{'label'} } @new_labeled_before;
@new_labeled_after = sort { $a->{'label'} cmp $b->{'label'} } @new_labeled_after;
@new_volume_before = sort { $a->{'label'} cmp $b->{'label'} } @new_volume_before;
@new_volume_after = sort { $a->{'label'} cmp $b->{'label'} } @new_volume_after;
my @result;
my @soon_before;
my @soon_after;
my @order_before;
my @order_after;
my @last_before;
my @last_after;
my $use;
if (@new_labeled_after && $self->{'scan_conf'}->{'new_labeled'} eq 'soon') {
push @soon_after, @new_labeled_after;
}
if (@new_labeled_before && $self->{'scan_conf'}->{'new_labeled'} eq 'soon') {
push @soon_before, @new_labeled_before;
}
if (@new_volume_after && $self->{'scan_conf'}->{'new_volume'} eq 'soon') {
push @soon_after, @new_volume_after;
}
if (@new_volume_before && $self->{'scan_conf'}->{'new_volume'} eq 'soon') {
push @soon_before, @new_volume_before;
}
@soon_before = sort { $a->{'label'} cmp $b->{'label'} } @soon_before;
@soon_after = sort { $a->{'label'} cmp $b->{'label'} } @soon_after;
if (@new_labeled_before && $self->{'scan_conf'}->{'new_labeled'} eq 'order') {
push @order_before, @new_labeled_before;
}
if (@new_labeled_after && $self->{'scan_conf'}->{'new_labeled'} eq 'order') {
push @order_after, @new_labeled_after;
}
if (@new_volume_before && $self->{'scan_conf'}->{'new_volume'} eq 'order') {
push @order_before, @new_volume_before;
}
if (@new_volume_after && $self->{'scan_conf'}->{'new_volume'} eq 'order') {
push @order_after, @new_volume_after;
}
if (@reusable_after) {
push @order_after, @reusable_after;
}
if (@reusable_before) {
push @order_before, @reusable_before;
}
@order_before = sort { $a->{'label'} cmp $b->{'label'} } @order_before;
@order_after = sort { $a->{'label'} cmp $b->{'label'} } @order_after;
if (@new_labeled_after && $self->{'scan_conf'}->{'new_labeled'} eq 'last') {
push @last_after, @new_labeled_after;
}
if (@new_labeled_before && $self->{'scan_conf'}->{'new_labeled'} eq 'last') {
push @last_before, @new_labeled_before;
}
if (@new_volume_after && $self->{'scan_conf'}->{'new_volume'} eq 'last') {
push @last_after, @new_volume_after;
}
if (@new_volume_before && $self->{'scan_conf'}->{'new_volume'} eq 'last') {
push @last_before, @new_volume_before;
}
@last_before = sort { $a->{'label'} cmp $b->{'label'} } @last_before;
@last_after = sort { $a->{'label'} cmp $b->{'label'} } @last_after;
push @result, @soon_after, @soon_before, @order_after, @order_before, @last_after, @last_before, @unknown, @error;
return \@result;
}
1;