X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=perl%2FAmanda%2FChanger%2Frobot.pm;h=dae6af016477daa0bf36643be5388c065a4d4eb4;hb=949b8910a5e23c4285d0b1aedacfc82a14dc97a5;hp=00822452887965e6220ba83191dca18dd4ddebb1;hpb=fd48f3e498442f0cbff5f3606c7c403d0566150e;p=debian%2Famanda diff --git a/perl/Amanda/Changer/robot.pm b/perl/Amanda/Changer/robot.pm index 0082245..dae6af0 100644 --- a/perl/Amanda/Changer/robot.pm +++ b/perl/Amanda/Changer/robot.pm @@ -1,4 +1,4 @@ -# Copyright (c) 2009,2010 Zmanda, Inc. All Rights Reserved. +# 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 @@ -68,6 +68,7 @@ See the amanda-changers(7) manpage for usage information. # as values. Each slot's hash has keys # state - SLOT_FULL/SLOT_EMPTY/SLOT_UNKNOWN # device_status - the status of the device +# device_error - the error message of the device # f_type - The f_type of the header # label - volume label, if known # barcode - volume barcode, if available @@ -193,7 +194,7 @@ sub new { } # eject-before-unload - my $ebu = $self->get_boolean_property($self->{'config'}, + my $ebu = $self->{'config'}->get_boolean_property( "eject-before-unload", 0); if (!defined $ebu) { return Amanda::Changer->make_error("fatal", undef, @@ -202,7 +203,7 @@ sub new { $self->{'eject_before_unload'} = $ebu; # fast-search - my $fast_search = $self->get_boolean_property($self->{'config'}, + my $fast_search = $self->{'config'}->get_boolean_property( "fast-search", 1); if (!defined $fast_search) { return Amanda::Changer->make_error("fatal", undef, @@ -266,7 +267,7 @@ sub new { } # status-interval, eject-delay, unload-delay - for my $propname qw(status-interval eject-delay unload-delay) { + for my $propname (qw(status-interval eject-delay unload-delay)) { next unless exists $config->{'properties'}->{$propname}; if (@{$config->{'properties'}->{$propname}->{'values'}} > 1) { return Amanda::Changer->make_error("fatal", undef, @@ -287,7 +288,7 @@ sub new { $self->{$key} = $time; } - my $ignore_barcodes = $self->get_boolean_property($self->{'config'}, + my $ignore_barcodes = $self->{'config'}->get_boolean_property( "ignore-barcodes", 0); if (!defined $ignore_barcodes) { return Amanda::Changer->make_error("fatal", undef, @@ -416,7 +417,7 @@ sub load_unlocked { if ($state->{'slots'}->{$slot}->{'state'} eq Amanda::Changer::SLOT_EMPTY) { return $self->make_error("failed", $params{'res_cb'}, - reason => "notfound", + reason => "empty", message => "slot $slot is empty"); } @@ -453,7 +454,7 @@ sub load_unlocked { } # otherwise, we can jump all the way to the end of this process - return $steps->{'check_device'}->(); + return $steps->{'start_polling'}->(); } # here is where we implement each of the drive-selection algorithms @@ -470,7 +471,7 @@ sub load_unlocked { @check_order = (@{$self->{'driveorder'}}); } else { # the constructor should detect this circumstance - die "invalid drive_choice"; + confess "invalid drive_choice"; } my %checked; @@ -586,7 +587,7 @@ sub load_unlocked { step check_device => sub { my $device_name = $self->{'drive2device'}->{$drive}; - die "drive $drive not found in drive2device" unless $device_name; # shouldn't happen + confess "drive $drive not found in drive2device" unless $device_name; # shouldn't happen $self->_debug("polling '$device_name' to see if it's ready"); @@ -617,9 +618,7 @@ sub load_unlocked { } elsif ($device->status & $DEVICE_STATUS_VOLUME_UNLABELED) { $label = undef; } else { - return $self->make_error("fatal", $params{'res_cb'}, - message => "while waiting for '$device_name' to become ready: " - . $device->error_or_status()); + $label = undef; } # success! @@ -637,14 +636,21 @@ sub load_unlocked { # update metadata with this new information $state->{'slots'}->{$slot}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'slots'}->{$slot}->{'device_status'} = $device->status; - if (defined $device->{'header'}) { - $state->{'slots'}->{$slot}->{'f_type'} = $device->{'header'}->{type}; + $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; } $state->{'slots'}->{$slot}->{'label'} = $label; if ($state->{'slots'}->{$slot}->{'barcode'}) { - $state->{'bc2lb'}->{$state->{'slots'}->{$slot}->{'barcode'}} = $label; + my $barcode = $state->{'slots'}->{$slot}->{'barcode'}; + my $old_label = $state->{'bc2lb'}->{$barcode}; + if ($label ne $old_label) { + $self->_debug("make_res: slot $slot"); + $self->_debug("update label '$label' for barcode '$barcode', old label was '$old_label'"); + } + $state->{'bc2lb'}->{$barcode} = $label; } return $self->make_error("failed", $params{'res_cb'}, @@ -659,8 +665,9 @@ sub load_unlocked { # update metadata with this new information $state->{'slots'}->{$slot}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'slots'}->{$slot}->{'device_status'} = $device->status; - if (defined $device->{'header'}) { - $state->{'slots'}->{$slot}->{'f_type'} = $device->{'header'}->{type}; + $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; } @@ -687,12 +694,18 @@ sub load_unlocked { $state->{'drives'}->{$drive}->{'label'} = $label; $state->{'drives'}->{$drive}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'drives'}->{$drive}->{'barcode'} = $state->{'slots'}->{$slot}->{'barcode'}; - #$state->{'slots'}->{$slot}->{'device_status'} = 9; - if ($label and $state->{'slots'}->{$slot}->{'barcode'}) { - $state->{'bc2lb'}->{$state->{'slots'}->{$slot}->{'barcode'}} = $label; + $state->{'slots'}->{$slot}->{'device_status'} = $device->status; + my $barcode = $state->{'slots'}->{$slot}->{'barcode'}; + if ($label and $barcode) { + my $old_label = $state->{'bc2lb'}->{$barcode}; + if (defined $old_label and $old_label ne $label) { + $self->_debug("load drive $drive slot $slot"); + $self->_debug("update label '$label' for barcode '$barcode', old label was '$old_label'"); + } + $state->{'bc2lb'}->{$barcode} = $label; } if ($params{'set_current'}) { - $self->_debug("setting current slot to $slot"); + $self->_debug("setting current slot to $slot"); $state->{'current_slot'} = $slot; } @@ -728,7 +741,8 @@ sub info_key_vendor_string { $self->{'interface'}->inquiry(make_cb(inquiry_cb => sub { my ($err, $info) = @_; - return $params{'info_cb'}->($err) if $err; + return $self->make_error("fatal", $params{'info_cb'}, + message => "$err") if $err; my $vendor_string = sprintf "%s %s", ($info->{'vendor id'} or ""), @@ -789,14 +803,14 @@ sub get_device { # (overridden by subclasses) my $device = Amanda::Device->new($device_name); if ($device->status != $DEVICE_STATUS_SUCCESS) { - return $self->make_error("failed", undef, - reason => "device", + return Amanda::Changer->make_error("fatal", undef, + reason => "unknown", message => "opening '$device_name': " . $device->error_or_status()); } if (my $err = $self->{'config'}->configure_device($device)) { - return $self->make_error("failed", undef, - reason => "device", + return Amanda::Changer->make_error("fatal", undef, + reason => "unknown", message => $err); } @@ -827,9 +841,13 @@ sub _set_label_unlocked { $state->{'drives'}->{$drive}->{'label'} = $label; if (defined $slot) { - delete $state->{'slots'}->{$slot}->{'unkknown_state'}; $state->{'slots'}->{$slot}->{'state'} = Amanda::Changer::SLOT_FULL; $state->{'slots'}->{$slot}->{'device_status'} = "".$dev->status; + if ($dev->status != $DEVICE_STATUS_SUCCESS) { + $state->{'slots'}->{$slot}->{'device_error'} = $dev->error; + } else { + $state->{'slots'}->{$slot}->{'device_error'} = undef; + } my $volume_header = $dev->volume_header; if (defined $volume_header) { $state->{'slots'}->{$slot}->{'f_type'} = "".$volume_header->{type}; @@ -839,6 +857,11 @@ sub _set_label_unlocked { $state->{'slots'}->{$slot}->{'label'} = $label; } if (defined $barcode) { + if (defined $state->{'bc2lb'}->{$barcode} and + $state->{'bc2lb'}->{$barcode} ne $label) { + my $old_label = $state->{'bc2lb'}->{$barcode}; + $self->_debug("update barcode '$barcode' to label '$label', old label was '$old_label'"); + } $state->{'bc2lb'}->{$barcode} = $label; } @@ -1089,6 +1112,7 @@ sub update_unlocked { while (my ($sl, $inf) = each %{$state->{'slots'}}) { if ($inf->{'label'} and $inf->{'label'} eq $label) { delete $inf->{'device_status'}; + delete $inf->{'device_error'}; delete $inf->{'f_type'}; delete $inf->{'label'}; } @@ -1151,6 +1175,7 @@ sub update_unlocked { if (!defined $state->{'slots'}->{$slot}->{'barcode'}) { $state->{'slots'}->{$slot}->{'label'} = undef; $state->{'slots'}->{$slot}->{'device_status'} = undef; + $state->{'slots'}->{$slot}->{'device_error'} = undef; $state->{'slots'}->{$slot}->{'f_type'} = undef; if (defined $state->{'slots'}->{$slot}->{'loaded_in'}) { my $drive = $state->{'slots'}->{$slot}->{'loaded_in'}; @@ -1229,6 +1254,7 @@ sub inventory_unlocked { $i->{'slot'} = $slot_name; $i->{'state'} = $slot->{'state'}; $i->{'device_status'} = $slot->{'device_status'}; + $i->{'device_error'} = $slot->{'device_error'}; $i->{'f_type'} = $slot->{'f_type'}; $i->{'label'} = $slot->{'label'}; $i->{'barcode'} = $slot->{'barcode'} @@ -1285,10 +1311,14 @@ sub move_unlocked { message => "slot $from_slot is empty"); } - if (defined $state->{'slots'}->{$from_slot}->{'loaded_in'}) { - return $self->make_error("failed", $params{'finished_cb'}, - reason => "invalid", - message => "slot $from_slot is currently loaded"); + my $in_drive = $state->{'slots'}->{$from_slot}->{'loaded_in'}; + if (defined $in_drive) { + my $info = $state->{'drives'}->{$in_drive}; + if ($info->{'res_info'} and $self->_res_info_verify($info->{'res_info'})) { + return $self->make_error("failed", $params{'finished_cb'}, + reason => "invalid", + message => "slot $from_slot is currently loaded and reserved"); + } } if ($state->{'slots'}->{$to_slot}->{'state'} == Amanda::Changer::SLOT_FULL) { @@ -1305,17 +1335,49 @@ sub move_unlocked { return $params{'finished_cb'}->($err) if $err; # update metadata - $state->{'slots'}->{$to_slot} = { %{ $state->{'slots'}->{$from_slot} } }; - $state->{'slots'}->{$from_slot}->{'state'} = - Amanda::Changer::SLOT_EMPTY; - $state->{'slots'}->{$from_slot}->{'device_status'} = undef; - $state->{'slots'}->{$from_slot}->{'f_type'} = undef; - $state->{'slots'}->{$from_slot}->{'label'} = undef; - $state->{'slots'}->{$from_slot}->{'barcode'} = undef; + if ($from_slot ne $to_slot) { + my $f = $state->{'slots'}->{$from_slot}; + my $t = $state->{'slots'}->{$to_slot}; + + $t->{'device_status'} = $f->{'device_status'}; + $f->{'device_status'} = undef; + + $t->{'state'} = $f->{'state'}; + $f->{'state'} = Amanda::Changer::SLOT_EMPTY; + + $t->{'f_type'} = $f->{'f_type'}; + $f->{'f_type'} = undef; + + $t->{'label'} = $f->{'label'}; + $f->{'label'} = undef; + + $t->{'barcode'} = $f->{'barcode'}; + $f->{'barcode'} = undef; + } + + # properly represent the unload operation, if it was performed + if (defined $in_drive) { + $state->{'slots'}->{$from_slot}->{'loaded_in'} = undef; + $state->{'slots'}->{$to_slot}->{'loaded_in'} = undef; + + $state->{'drives'}->{$in_drive}->{'state'} = + Amanda::Changer::SLOT_EMPTY; + $state->{'drives'}->{$in_drive}->{'label'} = undef; + $state->{'drives'}->{$in_drive}->{'barcode'} = undef; + $state->{'drives'}->{$in_drive}->{'orig_slot'} = undef; + } $params{'finished_cb'}->(); }); - $self->{'interface'}->transfer($from_slot, $to_slot, $transfer_complete); + + # if the source slot is loaded, then this is just a directed unload operation; + # otherwise, it's a transfer. + if (defined $in_drive) { + Amanda::Debug::debug("move(): unloading drive $in_drive to slot $to_slot"); + $self->{'interface'}->unload($in_drive, $to_slot, $transfer_complete); + } else { + $self->{'interface'}->transfer($from_slot, $to_slot, $transfer_complete); + } } ## @@ -1489,6 +1551,7 @@ sub _with_updated_state { $new_slots->{$slot} = { state => Amanda::Changer::SLOT_EMPTY, device_status => undef, + device_error => undef, f_type => undef, label => undef, barcode => undef, @@ -1505,6 +1568,7 @@ sub _with_updated_state { $new_slots->{$slot} = { state => Amanda::Changer::SLOT_FULL, device_status => $state->{'slots'}->{$slot}->{device_status}, + device_error => $state->{'slots'}->{$slot}->{device_error}, f_type => $state->{'slots'}->{$slot}->{f_type}, label => $label, barcode => $info->{'barcode'}, @@ -1521,6 +1585,7 @@ sub _with_updated_state { $new_slots->{$slot} = { state => Amanda::Changer::SLOT_FULL, device_status => undef, + device_error => undef, f_type => undef, label => undef, barcode => undef, @@ -1616,6 +1681,7 @@ sub _with_updated_state { $state->{'slots'}->{$info->{'orig_slot'}} = { state => $info->{'state'}, device_status => $old_state->{'device_status'}, + device_error => $old_state->{'device_error'}, f_type => $old_state->{'f_type'}, label => $info->{'label'}, barcode => $info->{'barcode'}, @@ -1839,18 +1905,33 @@ sub status { synchronized($self->{'lock'}, $status_cb, sub { my ($status_cb) = @_; + my ($counter) = 120; + + my $sys_cb; + my $run_mtx = make_cb(run_mtx => sub { + my @nobarcode = ('nobarcode') if $self->{'ignore_barcodes'}; + $self->_run_system_command($sys_cb, + $self->{'mtx'}, "-f", $self->{'device_name'}, @nobarcode, + 'status'); + }); - my $sys_cb = make_cb(sys_cb => sub { + $sys_cb = make_cb(sys_cb => sub { my ($exitstatus, $output) = @_; if ($exitstatus != 0) { my $err = $output; # if it's a regular SCSI error, just show the sense key my ($sensekey) = ($err =~ /mtx: Request Sense: Sense Key=(.*)\n/); $err = "SCSI error; Sense Key=$sensekey" if $sensekey; + $counter--; + if ($sensekey eq "Not Ready" and $counter > 0) { + debug("$output"); + return Amanda::MainLoop::call_after(1000, $run_mtx); + } return $status_cb->("error from mtx: " . $err, {}); } else { my %status; for my $line (split '\n', $output) { + debug("mtx: $line"); my ($slot, $ie, $slinfo); # drives (data transfer elements) @@ -1895,9 +1976,7 @@ sub status { } }); - my @nobarcode = ('nobarcode') if $self->{'ignore_barcodes'}; - $self->_run_system_command($sys_cb, - $self->{'mtx'}, "-f", $self->{'device_name'}, @nobarcode, 'status'); + $run_mtx->(); }); } @@ -1964,12 +2043,12 @@ sub _run_system_command { my ($readfd, $writefd) = POSIX::pipe(); if (!defined($writefd)) { - die("Error creating pipe: $!"); + confess("Error creating pipe: $!"); } my $pid = fork(); if (!defined($pid) or $pid < 0) { - die("Can't fork to run changer script: $!"); + confess("Can't fork to run changer script: $!"); } if (!$pid) {