-# 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
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};
}
taperscan => $params{'taperscan'},
feedback => $params{'feedback'},
debug => $decide_debug,
+ eject_volume => $params{'eject_volume'},
write_timestamp => undef,
started => 0,
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,
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'};
$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
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();
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;
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();
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)
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 {
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
$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
# 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;
}
# 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';
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
# _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";
}
}
}
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;
$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;
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) {
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
$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,
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
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};
}
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'};
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'}");
} 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");
$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();
});
} 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();