Imported Upstream version 3.3.2
[debian/amanda] / perl / Amanda / Taper / Scribe.pm
index dbfce4a493e1de862bcbf4ee75bf9d9e54a88abb..31831b754451a62144bc9598c39962d6f48ce9f5 100644 (file)
@@ -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
@@ -442,7 +442,7 @@ sub new {
     my %params = @_;
 
     my $decide_debug = $Amanda::Config::debug_taper || $params{'debug'};
-    for my $rq_param qw(taperscan feedback) {
+    for my $rq_param (qw(taperscan feedback)) {
        croak "required parameter '$rq_param' mising"
            unless exists $params{$rq_param};
     }
@@ -451,6 +451,7 @@ sub new {
        taperscan => $params{'taperscan'},
        feedback => $params{'feedback'},
        debug => $decide_debug,
+       eject_volume => $params{'eject_volume'},
        write_timestamp => undef,
        started => 0,
 
@@ -463,6 +464,7 @@ sub new {
        device => undef,
        device_size => undef,
        device_at_eom => undef, # device still exists, but is full
+       close_volume => undef,
 
         # callback passed to start_dump
        dump_cb => undef,
@@ -491,12 +493,12 @@ sub start {
     my $self = shift;
     my %params = @_;
 
-    for my $rq_param qw(write_timestamp finished_cb) {
+    for my $rq_param (qw(write_timestamp finished_cb)) {
        croak "required parameter '$rq_param' missing"
            unless exists $params{$rq_param};
     }
 
-    die "scribe already started" if $self->{'started'};
+    confess "scribe already started" if $self->{'started'};
 
     $self->dbg("starting");
     $self->{'write_timestamp'} = $params{'write_timestamp'};
@@ -525,7 +527,7 @@ sub quit {
        $self->dbg("quitting");
 
        if ($self->{'xfer'}) {
-           die "Scribe cannot quit while a transfer is active";
+           confess "Scribe cannot quit while a transfer is active";
            # Supporting this would be complicated:
            # - cancel the xfer and wait for it to complete
            # - ensure that the taperscan not be started afterward
@@ -588,7 +590,7 @@ sub check_data_path {
     my $device = $self->get_device();
 
     if (!defined $device) {
-       die "no device is available to check the datapath";
+       confess "no device is available to check the datapath";
     }
 
     my $use_directtcp = $device->directtcp_supported();
@@ -615,16 +617,16 @@ sub get_xfer_dest {
     my $self = shift;
     my %params = @_;
 
-    for my $rq_param qw(max_memory) {
+    for my $rq_param (qw(max_memory)) {
        croak "required parameter '$rq_param' missing"
            unless exists $params{$rq_param};
     }
 
-    die "not yet started"
+    confess "not yet started"
        unless $self->{'write_timestamp'} and $self->{'started'};
-    die "xfer element already returned"
+    confess "xfer element already returned"
        if ($self->{'xdt'});
-    die "xfer already running"
+    confess "xfer already running"
        if ($self->{'xfer'});
 
     $self->{'xfer'} = undef;
@@ -654,7 +656,7 @@ sub get_xfer_dest {
 
     my $xdt_first_dev = $self->get_device();
     if (!defined $xdt_first_dev) {
-       die "no device is available to create an xfer_dest";
+       confess "no device is available to create an xfer_dest";
     }
     my $leom_supported = $xdt_first_dev->property_get("leom");
     my $use_directtcp = $xdt_first_dev->directtcp_supported();
@@ -745,7 +747,7 @@ sub start_dump {
     my $self = shift;
     my %params = @_;
 
-    die "no xfer dest set up; call get_xfer_dest first"
+    confess "no xfer dest set up; call get_xfer_dest first"
         unless defined $self->{'xdt'};
 
     # get the header ready for writing (totalparts was set by the caller)
@@ -765,29 +767,23 @@ sub cancel_dump {
     my $self = shift;
     my %params = @_;
 
-    die "no xfer dest set up; call get_xfer_dest first"
+    confess "no xfer dest set up; call get_xfer_dest first"
        unless defined $self->{'xdt'};
 
     # set up the dump_cb for when this dump is done, and keep the xfer
     $self->{'dump_cb'} = $params{'dump_cb'};
     $self->{'xfer'} = $params{'xfer'};
 
-    # XXX The cancel should call dump_cb, but right now the xfer stays hung in
-    # accept.  So we leave the xfer to its hang, and dump_cb is called and xdt
-    # and xfer are set to undef.  This should be fixed in 3.2.
+    # The cancel will can dump_cb.
 
     $self->{'xfer'}->cancel();
 
-    $self->{'dump_cb'}->(
-       result => "FAILED",
-       device_errors => [],
-       config_denial_message => undef,
-       size => 0,
-       duration => 0.0,
-       total_duration => 0,
-       nparts => 0);
-    $self->{'xdt'} = undef;
-    $self->{'xfer'} = undef;
+}
+
+sub close_volume {
+    my $self = shift;
+
+    $self->{'close_volume'} = 1;
 }
 
 sub get_bytes_written {
@@ -813,6 +809,11 @@ sub _start_part {
        return
     }
 
+    if ($self->{'close_volume'}) {
+       $self->{'close_volume'} = undef;
+       return $self->_get_new_volume();
+    }
+
     # we need an actual, permitted device at this point, so if we don't have
     # one, then defer this start_part call until we do.  The device may still
     # exist, but be at EOM, if the last dump failed at EOM and was not retried
@@ -873,7 +874,7 @@ sub _xmsg_part_done {
        $self->dbg("not notifying for empty, successful part");
     } else {
        # double-check partnum
-       die "Part numbers do not match!"
+       confess "Part numbers do not match!"
            unless ($self->{'dump_header'}->{'partnum'} == $msg->{'partnum'});
 
        # notify
@@ -939,11 +940,12 @@ sub _xmsg_part_done {
            # if the part was unsuccessful, but the xfer dest has reason to believe
            # this is not due to EOM, then the dump is done
            if (!$msg->{'successful'}) {
-               my $msg = "unknown error while dumping";
                if ($self->{'device'}->status() != $DEVICE_STATUS_SUCCESS) {
                    $msg = $self->{'device'}->error_or_status();
+                   $self->_operation_failed(device_error => $msg);
+               } else {
+                   $self->_operation_failed();
                }
-               $self->_operation_failed(device_error => $msg);
                return;
            }
 
@@ -990,7 +992,8 @@ sub _dump_done {
 
     # determine the correct final status - DONE if we're done, PARTIAL
     # if we've started writing to the volume, otherwise FAILED
-    if (@{$self->{'device_errors'}} or $self->{'config_denial_message'}) {
+    if (@{$self->{'device_errors'}} or $self->{'config_denial_message'} or
+       !$self->{'last_part_successful'}) {
        $result = $self->{'started_writing'}? 'PARTIAL' : 'FAILED';
     } else {
        $result = 'DONE';
@@ -1031,7 +1034,7 @@ sub _operation_failed {
 
     my $error_message = $params{'device_error'}
                     || $params{'config_denial_message'}
-                    || 'no reason';
+                    || 'input error';
     $self->dbg("operation failed: $error_message");
 
     # tuck the message away as desired
@@ -1052,7 +1055,7 @@ sub _operation_failed {
             # _dump_done constructs the dump_cb from $self parameters
             $self->_dump_done();
         } else {
-            die "error with no callback to handle it: $error_message";
+            confess "error with no callback to handle it: $error_message";
         }
     }
 }
@@ -1063,11 +1066,13 @@ sub _release_reservation {
     my $self = shift;
     my %params = @_;
     my @errors;
+    my $do_eject = 0;
 
     my ($label, $fm, $kb);
 
     # if we've already written a volume, log it
     if ($self->{'device'} and defined $self->{'device'}->volume_label) {
+       $do_eject = 1 if $self->{'eject_volume'};
        $label = $self->{'device'}->volume_label();
        $fm = $self->{'device'}->file();
        $kb = $self->{'device_size'} / 1024;
@@ -1088,7 +1093,7 @@ sub _release_reservation {
     $self->{'device'} = undef;
     $self->{'device_at_eom'} = 0;
 
-    $self->{'reservation'}->release(finished_cb => sub {
+    $self->{'reservation'}->release(eject => $do_eject, finished_cb => sub {
        my ($err) = @_;
        push @errors, "$err" if $err;
 
@@ -1352,11 +1357,6 @@ sub _device_start {
        if ($is_new) {
            # generate the new label and write it to the tapelist file
            $tl->reload(1);
-           ($new_label, my $err) = $reservation->make_new_tape_label();
-           if (!defined $new_label) {
-               $tl->unlock();
-               return $finished_cb->($err);
-           }
            if (!$meta) {
                ($meta, $err) = $reservation->make_new_meta_label();
                if (defined $err) {
@@ -1364,13 +1364,24 @@ sub _device_start {
                    return $finished_cb->($err);
                }
            }
-           $tl->add_tapelabel('0', $new_label, undef, $meta, $reservation->{'barcode'});
+           ($new_label, my $err) = $reservation->make_new_tape_label(
+                                       meta => $meta);
+           if (!defined $new_label) {
+               $tl->unlock();
+               return $finished_cb->($err);
+           }
+           $tl->add_tapelabel('0', $new_label, undef, 1, $meta,
+                              $reservation->{'barcode'});
            $tl->write();
            $self->dbg("generate new label '$new_label'");
-       } elsif (!defined $meta) {
+       } else {
            $tl->reload(0);
            my $tle = $tl->lookup_tapelabel($new_label);
-           my $meta = $tle->{'meta'};
+           $meta = $tle->{'meta'} if !defined $meta && $tle->{'meta'};
+           my $barcode = $tle->{'barcode'};
+           if (defined $barcode and $barcode ne $reservation->{'barcode'}) {
+               return $finished_cb->("tapelist for label '$new_label' have barcode '$barcode' but changer report '" . $reservation->{'barcode'} . "'");
+           }
        }
 
        # write the label to the device
@@ -1390,7 +1401,8 @@ sub _device_start {
        $meta = $tle->{'meta'} if !$meta && $tle->{'meta'};
        $tl->remove_tapelabel($new_label);
        $tl->add_tapelabel($self->{'write_timestamp'}, $new_label,
-                          $tle? $tle->{'comment'} : undef, 1, $meta);
+                          $tle? $tle->{'comment'} : undef, 1, $meta,
+                          $reservation->{'barcode'}, $device->block_size/1024);
        $tl->write();
 
        $reservation->set_meta_label(meta => $meta,
@@ -1559,6 +1571,7 @@ sub scribe_notif_tape_done {
 package Amanda::Taper::Scribe::DevHandling;
 use Amanda::MainLoop;
 use Carp;
+use Amanda::Debug qw( :logging );
 
 # This class handles scanning for volumes, requesting permission for those
 # volumes (the driver likes to feel like it's in control), and providing those
@@ -1622,7 +1635,7 @@ sub quit {
     my $self = shift;
     my %params = @_;
 
-    for my $rq_param qw(finished_cb) {
+    for my $rq_param (qw(finished_cb)) {
        croak "required parameter '$rq_param' mising"
            unless exists $params{$rq_param};
     }
@@ -1668,7 +1681,7 @@ sub get_volume {
     my $self = shift;
     my (%params) = @_;
 
-    die "already processing a volume request"
+    confess "already processing a volume request"
        if ($self->{'volume_cb'});
 
     $self->{'volume_cb'} = $params{'volume_cb'};
@@ -1721,22 +1734,37 @@ sub _start_scanning {
                if ($params{'label'}) {
                    $self->{'feedback'}->scribe_notif_log_info(
                        message => "Slot $params{'slot'} with label $params{'label'} is not labelable ");
+               } elsif ($params{'empty'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is empty, autolabel not set");
+               } elsif ($params{'non_amanda'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is a non-amanda volume, autolabel not set");
+               } elsif ($params{'volume_error'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is a volume in error: $params{'err'}, autolabel not set");
+               } elsif ($params{'not_success'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "Slot $params{'slot'} is a device in error: $params{'err'}, autolabel not set");
+               } elsif ($params{'err'}) {
+                   $self->{'feedback'}->scribe_notif_log_info(
+                       message => "$params{'err'}");
                } else {
                    $self->{'feedback'}->scribe_notif_log_info(
                        message => "Slot $params{'slot'} without label is not labelable ");
                }
            } elsif ($params{'empty'}) {
                $self->{'feedback'}->scribe_notif_log_info(
-                   message => "Slot $params{'slot'} is empty");
+                   message => "Slot $params{'slot'} is empty, autolabel disabled");
            } elsif ($params{'non_amanda'}) {
                $self->{'feedback'}->scribe_notif_log_info(
-                   message => "Slot $params{'slot'} is a non-amanda volume");
+                   message => "Slot $params{'slot'} is a non-amanda volume, autolabel disabled");
            } elsif ($params{'volume_error'}) {
                $self->{'feedback'}->scribe_notif_log_info(
-                   message => "Slot $params{'slot'} is a volume in error: $params{'err'}");
+                   message => "Slot $params{'slot'} is a volume in error: $params{'err'}, autolabel disabled");
            } elsif ($params{'not_success'}) {
                $self->{'feedback'}->scribe_notif_log_info(
-                   message => "Slot $params{'slot'} is a device in error: $params{'err'}");
+                   message => "Slot $params{'slot'} is a device in error: $params{'err'}, autolabel disabled");
            } elsif ($params{'err'}) {
                $self->{'feedback'}->scribe_notif_log_info(
                    message => "$params{'err'}");
@@ -1746,6 +1774,9 @@ sub _start_scanning {
            } elsif (!defined $params{'label'}) {
                $self->{'feedback'}->scribe_notif_log_info(
                    message => "Slot $params{'slot'} without label can be labeled");
+           } elsif ($params{'relabeled'}) {
+               $self->{'feedback'}->scribe_notif_log_info(
+                   message => "Slot $params{'slot'} with label $params{'label'} will be relabeled");
            } else {
                $self->{'feedback'}->scribe_notif_log_info(
                    message => "Slot $params{'slot'} with label $params{'label'} is usable");
@@ -1761,15 +1792,12 @@ sub _start_scanning {
        $self->{'scan_running'} = 0;
        $self->{'scan_finished'} = 1;
 
-       if ($error) {
-           $self->{'scan_error'} = $error;
-       } else {
-           $self->{'reservation'} = $reservation;
-           $self->{'device'} = $reservation->{'device'};
-           $self->{'volume_label'} = $volume_label;
-           $self->{'access_mode'} = $access_mode;
-           $self->{'is_new'} = $is_new;
-       }
+       $self->{'scan_error'} = $error;
+       $self->{'reservation'} = $reservation;
+       $self->{'device'} = $reservation->{'device'} if $reservation;
+       $self->{'volume_label'} = $volume_label;
+       $self->{'access_mode'} = $access_mode;
+       $self->{'is_new'} = $is_new;
 
        $self->_maybe_callback();
     });
@@ -1799,10 +1827,10 @@ sub _start_request {
            } elsif ($params{'cause'} eq 'error') {
                $self->{'error_denial_message'} = $params{'message'};
            } else {
-               die "bad cause '" . $params{'cause'} . "'";
+               confess "bad cause '" . $params{'cause'} . "'";
            }
        } elsif (!defined $params{'allow'}) {
-           die "no allow or cause defined";
+           confess "no allow or cause defined";
        }
 
        $self->_maybe_callback();