X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=perl%2FAmanda%2FChanger%2Frobot.pm;fp=perl%2FAmanda%2FChanger%2Frobot.pm;h=9ba2a5890d63433ea1e40984e8f5a0c442728c77;hb=d28952249e392eb31bc8eecc53f6c477f30c617b;hp=dae6af016477daa0bf36643be5388c065a4d4eb4;hpb=949b8910a5e23c4285d0b1aedacfc82a14dc97a5;p=debian%2Famanda diff --git a/perl/Amanda/Changer/robot.pm b/perl/Amanda/Changer/robot.pm index dae6af0..9ba2a58 100644 --- a/perl/Amanda/Changer/robot.pm +++ b/perl/Amanda/Changer/robot.pm @@ -1,8 +1,9 @@ # Copyright (c) 2009-2012 Zmanda, Inc. All Rights Reserved. # -# This library is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License version 2.1 as -# published by the Free Software Foundation. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +#* License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY @@ -27,6 +28,7 @@ use vars qw( @ISA ); use Data::Dumper; use File::Path; use Amanda::Paths; +use Amanda::Config qw( :init ); use Amanda::MainLoop qw( :GIOCondition make_cb define_steps step ); use Amanda::Config qw( :getconf ); use Amanda::Debug qw( debug warning ); @@ -59,7 +61,7 @@ See the amanda-changers(7) manpage for usage information. # drives - see below # drive_lru - recently used drives, least recent first # bc2lb - hash mapping known barcodes to known labels -# current_slot - the current slot +# current_slot - hash of the current slot # last_operation_time - time the last operation finished # last_operation_delay - required delay for that operation # last_status - last time a 'status' command finished @@ -83,7 +85,10 @@ See the amanda-changers(7) manpage for usage information. # label - volume label # barcode - volume barcode # orig_slot - slot from which this tape was loaded - +# +## The 'current_slot' key is also a hash by config name, the values of +# which are the current slot for the config. +# # # LOCKING # # This package uses Amanda::Changer's with_locked_state to lock a statefile and @@ -130,6 +135,7 @@ sub new { # set below from properties statefile => undef, + 'lock-timeout' => undef, drive2device => {}, # { drive => device name } driveorder => [], # order of tape-device properties drive_choice => 'lru', @@ -161,7 +167,7 @@ sub new { $self->{'statefile'} = "$localstatedir/amanda/$safe_filename"; } $self->_debug("using statefile '$self->{statefile}'"); - + $self->{'lock-timeout'} = $config->{'lock-timeout'}; # figure out the drive number to device name mapping if (exists $config->{'tapedev'} and $config->{'tapedev'} ne '' @@ -337,24 +343,32 @@ sub load_unlocked { if (exists $params{'relative_slot'}) { if ($params{'relative_slot'} eq "next") { if (exists $params{'slot'}) { - $slot = $self->_get_next_slot($state, $params{'slot'}); - $self->_debug("loading next relative to $params{slot}: $slot"); + $slot = $self->_get_next_slot($state, $params{'slot'}, $params{'except_slots'}); + if (defined $slot) { + $self->_debug("loading next relative to $params{slot}: $slot"); + } else { + $self->_debug("no next slot relative to $params{slot}"); + } } else { - $slot = $self->_get_next_slot($state, $state->{'current_slot'}); - $self->_debug("loading next relative to current slot: $slot"); + $slot = $self->_get_next_slot($state, $state->{'current_slot'}{get_config_name()}, $params{'except_slots'}); + if (defined $slot) { + $self->_debug("loading next relative to current slot: $slot"); + } else { + $self->_debug("no next relative to current slot"); + } } - if ($slot == -1) { + if (defined $slot && $slot == -1) { return $self->make_error("failed", $params{'res_cb'}, reason => "invalid", message => "could not find next slot"); } } elsif ($params{'relative_slot'} eq "current") { - $slot = $state->{'current_slot'}; - if ($slot == -1) { + $slot = $state->{'current_slot'}{get_config_name()}; + if (defined $slot && $slot == -1) { # seek to the first slot - $slot = $self->_get_next_slot($state, $state->{'current_slot'}); + $slot = $self->_get_next_slot($state, $state->{'current_slot'}{get_config_name()}, $params{'except_slots'}); } - if ($slot == -1) { + if (defined $slot && $slot == -1) { return $self->make_error("failed", $params{'res_cb'}, reason => "invalid", message => "no current slot"); @@ -396,6 +410,30 @@ sub load_unlocked { message => "no 'slot' or 'label' specified to load()"); } + if (defined $slot and !exists $state->{'slots'}->{$slot}) { + return $self->make_error("failed", $params{'res_cb'}, + reason => "invalid", + message => "invalid slot '$slot'"); + } + + if (!defined $slot) { + my $all_empty = 1; + if (exists $params{'except_slots'}) { + for my $xslot (keys %{ $params{'except_slots'} }) { + if ($state->{'slots'}->{$xslot}->{'state'} ne Amanda::Changer::SLOT_EMPTY) { + $all_empty = 0; + } + } + if ($all_empty) { + return $self->make_error("failed", $params{'res_cb'}, + reason => "notfound", + message => "all slots are empty"); + } + } + return $self->make_error("failed", $params{'res_cb'}, + reason => "notfound", + message => "all slots have been loaded"); + } if (!$self->_is_slot_allowed($slot)) { if (exists $params{'label'}) { return $self->make_error("failed", $params{'res_cb'}, @@ -410,14 +448,28 @@ sub load_unlocked { } if (exists $params{'except_slots'} and exists $params{'except_slots'}->{$slot}) { - return $self->make_error("failed", $params{'res_cb'}, - reason => "notfound", - message => "all slots have been loaded"); + # if all slots in except_slots are EMPTY + my $all_empty = 1; + for my $xslot (keys %{ $params{'except_slots'} }) { + if ($state->{'slots'}->{$xslot}->{'state'} ne Amanda::Changer::SLOT_EMPTY) { + $all_empty = 0; + } + } + if ($all_empty) { + return $self->make_error("failed", $params{'res_cb'}, + reason => "notfound", + message => "all slots are empty"); + } else { + return $self->make_error("failed", $params{'res_cb'}, + reason => "notfound", + message => "all slots have been loaded"); + } } if ($state->{'slots'}->{$slot}->{'state'} eq Amanda::Changer::SLOT_EMPTY) { return $self->make_error("failed", $params{'res_cb'}, reason => "empty", + slot => $slot, message => "slot $slot is empty"); } @@ -636,9 +688,13 @@ sub load_unlocked { # update metadata with this new information $state->{'slots'}->{$slot}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'slots'}->{$slot}->{'device_status'} = $device->status; - $state->{'slots'}->{$slot}->{'device_error'} = $device->error; - if (defined $device->{'volume_header'}) { - $state->{'slots'}->{$slot}->{'f_type'} = $device->{'volume_header'}->{type}; + if ($device->status == $DEVICE_STATUS_SUCCESS) { + $state->{'slots'}->{$slot}->{'device_error'} = undef; + } else { + $state->{'slots'}->{$slot}->{'device_error'} = $device->error; + } + if (defined $device->volume_header) { + $state->{'slots'}->{$slot}->{'f_type'} = $device->volume_header->{type}; } else { $state->{'slots'}->{$slot}->{'f_type'} = undef; } @@ -665,9 +721,13 @@ sub load_unlocked { # update metadata with this new information $state->{'slots'}->{$slot}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'slots'}->{$slot}->{'device_status'} = $device->status; - $state->{'slots'}->{$slot}->{'device_error'} = $device->error; - if (defined $device->{'volume_header'}) { - $state->{'slots'}->{$slot}->{'f_type'} = $device->{'volume_header'}->{type}; + if ($device->status == $DEVICE_STATUS_SUCCESS) { + $state->{'slots'}->{$slot}->{'device_error'} = undef; + } else { + $state->{'slots'}->{$slot}->{'device_error'} = $device->error; + } + if (defined $device->volume_header) { + $state->{'slots'}->{$slot}->{'f_type'} = $device->volume_header->{type}; } else { $state->{'slots'}->{$slot}->{'f_type'} = undef; } @@ -681,6 +741,16 @@ sub load_unlocked { message => "Found unlabeled tape while looking for '$params{label}'"); } + if (defined $self->{'tapelist'}) { + my $tle = $self->{'tapelist'}->lookup_tapelabel($label); + if (defined $tle and defined $tle->{'barcode'} and + defined $state->{'slots'}->{$slot}->{'barcode'} and + $state->{'slots'}->{$slot}->{'barcode'} ne $tle->{'barcode'}) { + return $self->make_error("failed", $params{'res_cb'}, + reason => "device", + message => "Slot $slot, label '$label', mismatch barcode between changer '$state->{'slots'}->{$slot}->{'barcode'}' and tapelist file '$tle->{'barcode'}'"); + } + } my $res = Amanda::Changer::robot::Reservation->new($self, $slot, $drive, $device, $state->{'slots'}->{$slot}->{'barcode'}); @@ -695,6 +765,16 @@ sub load_unlocked { $state->{'drives'}->{$drive}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'drives'}->{$drive}->{'barcode'} = $state->{'slots'}->{$slot}->{'barcode'}; $state->{'slots'}->{$slot}->{'device_status'} = $device->status; + if ($device->status == $DEVICE_STATUS_SUCCESS) { + $state->{'slots'}->{$slot}->{'device_error'} = undef; + } else { + $state->{'slots'}->{$slot}->{'device_error'} = $device->error; + } + if (defined $device->volume_header) { + $state->{'slots'}->{$slot}->{'f_type'} = $device->volume_header->{type}; + } else { + $state->{'slots'}->{$slot}->{'f_type'} = undef; + } my $barcode = $state->{'slots'}->{$slot}->{'barcode'}; if ($label and $barcode) { my $old_label = $state->{'bc2lb'}->{$barcode}; @@ -706,7 +786,7 @@ sub load_unlocked { } if ($params{'set_current'}) { $self->_debug("setting current slot to $slot"); - $state->{'current_slot'} = $slot; + $state->{'current_slot'}{get_config_name()} = $slot; } return $params{'res_cb'}->(undef, $res); @@ -804,13 +884,13 @@ sub get_device { # (overridden by subclasses) my $device = Amanda::Device->new($device_name); if ($device->status != $DEVICE_STATUS_SUCCESS) { return Amanda::Changer->make_error("fatal", undef, - reason => "unknown", + reason => "device", message => "opening '$device_name': " . $device->error_or_status()); } if (my $err = $self->{'config'}->configure_device($device)) { return Amanda::Changer->make_error("fatal", undef, - reason => "unknown", + reason => "device", message => $err); } @@ -920,7 +1000,7 @@ sub reset_unlocked { my %params = @_; my $state = $params{'state'}; - $state->{'current_slot'} = $self->_get_next_slot($state, -1); + $state->{'current_slot'}{get_config_name()} = $self->_get_next_slot($state, -1); $params{'finished_cb'}->(); } @@ -1270,7 +1350,7 @@ sub inventory_unlocked { if $slot->{'ie'}; $i->{'current'} = 1 - if $slot_name eq $state->{'current_slot'}; + if $slot_name eq $state->{'current_slot'}{get_config_name()}; push @inv, $i; } @@ -1387,11 +1467,12 @@ sub move_unlocked { # the changer status has been updated) sub _get_next_slot { my $self = shift; - my ($state, $slot) = @_; + my ($state, $slot, $except_slots) = @_; my @nonempty = sort { $a <=> $b } grep { $state->{'slots'}->{$_}->{'state'} == Amanda::Changer::SLOT_FULL and $self->_is_slot_allowed($_) + and (!$except_slots || !$except_slots->{$_}) } keys(%{$state->{'slots'}}); my @higher = grep { $_ > $slot } @nonempty; @@ -1418,7 +1499,6 @@ sub _is_slot_allowed { return 0; } -# add a prefix and call Amanda::Debug::debug sub _debug { my $self = shift; my ($msg) = @_; @@ -1502,7 +1582,13 @@ sub _with_updated_state { $state->{'drives'} = {}; $state->{'drive_lru'} = []; $state->{'bc2lb'} = {}; - $state->{'current_slot'} = -1; + $state->{'current_slot'}{get_config_name()} = -1; + } + + if (defined $state->{'current_slot'} && + ref(\$state->{'current_slot'}) eq "SCALAR") { + my $current_slot = $state->{'current_slot'}; + $state->{'current_slot'} = {get_config_name() => $current_slot}; } # this is for testing ONLY! @@ -1698,8 +1784,9 @@ sub _with_updated_state { } } - if ($state->{'current_slot'} == -1) { - $state->{'current_slot'} = $self->_get_next_slot($state, -1); + if (!defined $state->{'current_slot'}{get_config_name()} || + $state->{'current_slot'}{get_config_name()} == -1) { + $state->{'current_slot'}{get_config_name()} = $self->_get_next_slot($state, -1); } $steps->{'done'}->();