step start_xfer => sub {
my ($err) = @_;
my $xfer_dest = $scribe->get_xfer_dest(
+ allow_split => 1,
max_memory => 64 * 1024,
can_cache_inform => 0,
part_size => 150 * 1024**2,
the dump should be split:
$xdest = $scribe->get_xfer_dest(
+ allow_split => $allow_split,
max_memory => $max_memory,
# .. splitting parameters
);
=over 4
+=item C<allow_split>
+
+this dle is allowed or not to split
+
=item C<part_size>
the split part size to use, or 0 for no splitting
use Amanda::Taper::Scribe qw( get_splitting_args_from_config );
my %splitting_args = get_splitting_args_from_config(
# Amanda dumptype configuration parameters,
+ dle_allow_split => ..,
dle_tape_splitsize => ..,
dle_split_diskbuffer => ..,
dle_fallback_splitsize => ..,
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,
# information for the current dumpfile
dump_header => undef,
retry_part_on_peom => undef,
+ allow_split => undef,
xfer => undef,
xdt => undef,
xdt_ready => 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};
}
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};
}
# set the callback
$self->{'dump_cb'} = undef;
$self->{'retry_part_on_peom'} = 1;
+ $self->{'allow_split'} = 0;
$self->{'start_part_on_xdt_ready'} = 0;
# start getting parameters together to determine what kind of splitting
my ($use_mem_cache, $disk_cache_dirname) = (0, undef);
my $can_cache_inform = $params{'can_cache_inform'};
my $part_cache_type = $params{'part_cache_type'} || 'none';
+ my $allow_split = $params{'allow_split'};
my $xdt_first_dev = $self->get_device();
if (!defined $xdt_first_dev) {
$self->{'retry_part_on_peom'} = 0;
}
+ if ($allow_split &&
+ ($can_cache_inform ||
+ !defined($part_cache_type) ||
+ $part_cache_type eq 'disk' ||
+ $part_cache_type eq 'memory' ||
+ $leom_supported)) {
+ $self->{'allow_split'} = 1;
+ } else {
+ $self->{'allow_split'} = 0;
+ }
+
+ $self->{'retry_part_on_peom'} = 0 if !$self->{'allow_split'};
+
debug("Amanda::Taper::Scribe preparing to write, part size $part_size, "
. "$dest_text ($dest_type) "
. ($leom_supported? " (LEOM supported)" : " (no LEOM)"));
$self->{'xfer'} = undef;
}
+sub close_volume {
+ my $self = shift;
+
+ $self->{'close_volume'} = 1;
+}
+
sub get_bytes_written {
my ($self) = @_;
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
}
# if the part failed..
- if (!$msg->{'successful'}) {
+ if (!$msg->{'successful'} || !$self->{'allow_split'}) {
# if no caching was going on, then the dump has failed
if (!$self->{'retry_part_on_peom'}) {
# mark this device as at EOM, since we are not going to look
if ($self->{'device'}->status() != $DEVICE_STATUS_SUCCESS) {
$msg = $self->{'device'}->error_or_status();
}
- $self->_operation_failed(device_error => $msg);
+ $self->_operation_failed(device_error => "$msg, splitting not enabled");
return;
}
# 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
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;
# notify the feedback that we've finished and released a tape
if ($label) {
- $self->{'feedback'}->scribe_notif_tape_done(
+ return $self->{'feedback'}->scribe_notif_tape_done(
volume_label => $label,
size => $kb * 1024,
- num_files => $fm);
+ num_files => $fm,
+ finished_cb => sub {
+ $params{'finished_cb'}->(@errors? join("; ", @errors) : undef);
+ });
}
$params{'finished_cb'}->(@errors? join("; ", @errors) : undef);
$new_scribe->{'dump_cb'} = $self->{'dump_cb'};
$new_scribe->{'dump_header'} = $self->{'dump_header'};
$new_scribe->{'retry_part_on_peom'} = $self->{'retry_part_on_peom'};
+ $new_scribe->{'allow_split'} = $self->{'allow_split'};
$new_scribe->{'split_method'} = $self->{'split_method'};
$new_scribe->{'xfer'} = $self->{'xfer'};
$new_scribe->{'xdt'} = $self->{'xdt'};
# inform the xdt about this new device before starting it
$self->{'xdt'}->use_device($device);
- my $result = $self->_device_start($reservation, $access_mode, $new_label, $is_new);
- if ($result == 0) {
- # try reading the label to see whether we erased the tape
- my $erased = 0;
- CHECK_READ_LABEL: {
+ my $cbX = sub {};
+ my $steps = define_steps
+ cb_ref => \$cbX;
+
+ step device_start => sub {
+ $self->_device_start($reservation, $access_mode, $new_label, $is_new,
+ $steps->{'device_started'});
+ };
+
+ step device_started => sub {
+ my $result = shift;
+
+ if ($result == 0) {
+ # try reading the label to see whether we erased the tape
+ my $erased = 0;
+ CHECK_READ_LABEL: {
# don't worry about erasing new tapes
- if ($is_new) {
- last CHECK_READ_LABEL;
- }
+ if ($is_new) {
+ last CHECK_READ_LABEL;
+ }
- $device->read_label();
+ $device->finish();
+ $device->read_label();
- # does the device think something is broken now?
- if (($device->status & ~$DEVICE_STATUS_VOLUME_UNLABELED)
- and !($device->status & $DEVICE_STATUS_VOLUME_UNLABELED)) {
- $erased = 1;
- last CHECK_READ_LABEL;
- }
+ # does the device think something is broken now?
+ if (($device->status & ~$DEVICE_STATUS_VOLUME_UNLABELED)
+ and !($device->status & $DEVICE_STATUS_VOLUME_UNLABELED)) {
+ $erased = 1;
+ last CHECK_READ_LABEL;
+ }
- # has the label changed?
- my $vol_label = $device->volume_label;
- if ((!defined $old_label and defined $vol_label)
- or (defined $old_label and !defined $vol_label)
- or (defined $old_label and $old_label ne $vol_label)) {
- $erased = 1;
- last CHECK_READ_LABEL;
- }
+ # has the label changed?
+ my $vol_label = $device->volume_label;
+ if ((!defined $old_label and defined $vol_label)
+ or (defined $old_label and !defined $vol_label)
+ or (defined $old_label and $old_label ne $vol_label)) {
+ $erased = 1;
+ last CHECK_READ_LABEL;
+ }
- # has the timestamp changed?
- my $vol_timestamp = $device->volume_time;
- if ((!defined $old_timestamp and defined $vol_timestamp)
- or (defined $old_timestamp and !defined $vol_timestamp)
- or (defined $old_timestamp and $old_timestamp ne $vol_timestamp)) {
- $erased = 1;
- last CHECK_READ_LABEL;
+ # has the timestamp changed?
+ my $vol_timestamp = $device->volume_time;
+ if ((!defined $old_timestamp and defined $vol_timestamp)
+ or (defined $old_timestamp and !defined $vol_timestamp)
+ or (defined $old_timestamp and $old_timestamp ne $vol_timestamp)) {
+ $erased = 1;
+ last CHECK_READ_LABEL;
+ }
}
+
+ $self->{'feedback'}->scribe_notif_new_tape(
+ error => "while labeling new volume: " . $device->error_or_status(),
+ volume_label => $erased? $new_label : undef);
+
+ $self->_get_new_volume();
+ return $cbX->();
+ } elsif ($result != 1) {
+ $self->{'feedback'}->scribe_notif_new_tape(
+ error => $result,
+ volume_label => undef);
+ $self->_get_new_volume();
+ return $cbX->();
}
- $self->{'feedback'}->scribe_notif_new_tape(
- error => "while labeling new volume: " . $device->error_or_status(),
- volume_label => $erased? $new_label : undef);
+ $new_label = $device->volume_label;
- return $self->_get_new_volume();
- } elsif ($result != 1) {
+ # success!
$self->{'feedback'}->scribe_notif_new_tape(
- error => $result,
- volume_label => undef);
- return $self->_get_new_volume();
- }
-
- $new_label = $device->volume_label;
+ error => undef,
+ volume_label => $new_label);
- # success!
- $self->{'feedback'}->scribe_notif_new_tape(
- error => undef,
- volume_label => $new_label);
+ $self->{'reservation'}->set_label(label => $new_label,
+ finished_cb => $steps->{'set_labelled'});
+ };
- # notify the changer that we've labeled the tape, and start the part.
- my $label_set_cb = make_cb(label_set_cb => sub {
+ step set_labelled => sub {
my ($err) = @_;
if ($err) {
$self->{'feedback'}->scribe_notif_log_info(
message => "Error from set_label: $err");
# fall through to start_part anyway...
}
- return $self->_start_part();
- });
- $self->{'reservation'}->set_label(label => $new_label,
- finished_cb => $label_set_cb);
+ $self->_start_part();
+ return $cbX->();
+ }
}
# return 0 for device->start error
# return a message for others error
sub _device_start {
my $self = shift;
- my ($reservation, $access_mode, $new_label, $is_new) = @_;
+ my ($reservation, $access_mode, $new_label, $is_new, $finished_cb) = @_;
my $device = $reservation->{'device'};
my $tl = $self->{'taperscan'}->{'tapelist'};
+ my $meta;
if (!defined $tl) { # For Mock::Taperscan in installcheck
if (!$device->start($access_mode, $new_label, $self->{'write_timestamp'})) {
- return 0;
+ return $finished_cb->(0);
} else {
- return 1;
+ return $finished_cb->(1);
}
}
- if ($is_new) {
- # generate the new label and write it to the tapelist file
- $tl->reload(1);
- ($new_label, my $err) = $self->{'taperscan'}->make_new_tape_label();
- if (!defined $new_label) {
- $tl->unlock();
- return $err;
- } else {
- $tl->add_tapelabel('0', $new_label, undef, 0, undef, $reservation->{'barcode'});
- $tl->write();
- }
- $self->dbg("generate new label '$new_label'");
- }
+ my $steps = define_steps
+ cb_ref => \$finished_cb;
+
+ step setup => sub {
+ return $reservation->get_meta_label(
+ finished_cb => $steps->{'got_meta_label'});
+ };
+
+ step got_meta_label => sub {
+ my ($err, $meta) = @_;
- # write the label to the device
- if (!$device->start($access_mode, $new_label, $self->{'write_timestamp'})) {
if ($is_new) {
- # remove the generated label from the tapelist file
+ # generate the new label and write it to the tapelist file
$tl->reload(1);
- $tl->remove_tapelabel($new_label);
+ if (!$meta) {
+ ($meta, $err) = $reservation->make_new_meta_label();
+ if (defined $err) {
+ $tl->unlock();
+ return $finished_cb->($err);
+ }
+ }
+ ($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();
- }
- return 0;
- }
+ $self->dbg("generate new label '$new_label'");
+ } elsif (!defined $meta) {
+ $tl->reload(0);
+ my $tle = $tl->lookup_tapelabel($new_label);
+ my $meta = $tle->{'meta'};
+ }
+
+ # write the label to the device
+ if (!$device->start($access_mode, $new_label, $self->{'write_timestamp'})) {
+ if ($is_new) {
+ # remove the generated label from the tapelist file
+ $tl->reload(1);
+ $tl->remove_tapelabel($new_label);
+ $tl->write();
+ }
+ return $finished_cb->(0);
+ }
- # rewrite the tapelist file
- $tl->reload(1);
- my $tle = $tl->lookup_tapelabel($new_label);
- $tl->remove_tapelabel($new_label);
- $tl->add_tapelabel($self->{'write_timestamp'}, $new_label,
- $tle? $tle->{'comment'} : undef, 1);
- $tl->write();
+ # rewrite the tapelist file
+ $tl->reload(1);
+ my $tle = $tl->lookup_tapelabel($new_label);
+ $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,
+ $reservation->{'barcode'}, $device->block_size/1024);
+ $tl->write();
+
+ $reservation->set_meta_label(meta => $meta,
+ finished_cb => $steps->{'set_meta_label'});
+ };
- return 1;
+ step set_meta_label => sub {
+ return $finished_cb->(1);
+ }
}
sub dbg {
sub get_splitting_args_from_config {
my %params = @_;
- use Data::Dumper;
my %splitting_args;
+ $splitting_args{'allow_split'} = 0;
# if dle_splitting is false, then we don't split - easy.
if (defined $params{'dle_allow_split'} and !$params{'dle_allow_split'}) {
- return ();
+ return %splitting_args;
}
# utility for below
my $fsusage = Amanda::Util::get_fs_usage($dirname);
confess "$dirname" if (!$fsusage);
- my $avail = $fsusage->{'blocks'} * $fsusage->{'bavail'};
+ my $avail = $fsusage->{'blocksize'} * $fsusage->{'bavail'};
if ($avail < $part_size) {
Amanda::Debug::debug("disk cache has $avail bytes available on $dirname, but " .
"needs $part_size");
# part cache type is memory unless we have a split_diskbuffer that fits the bill
if ($params{'part_size'}) {
+ $splitting_args{'allow_split'} = 1;
$params{'part_cache_type'} = 'memory';
if (defined $params{'dle_split_diskbuffer'}
and -d $params{'dle_split_diskbuffer'}) {
my $ps = $params{'part_size'};
my $pcms = $params{'part_cache_max_size'};
$ps = $pcms if (!defined $ps or (defined $pcms and $pcms < $ps));
+ $splitting_args{'allow_split'} = 1 if ((defined $ps and $ps > 0) or
+ $params{'leom_supported'});
# fail back from 'disk' to 'none' if the disk isn't set up correctly
if (defined $params{'part_cache_type'} and
}
sub scribe_notif_new_tape { }
-sub scribe_notif_tape_done { }
sub scribe_notif_part_done { }
sub scribe_notif_log_info { }
+sub scribe_notif_tape_done {
+ my $self = shift;
+ my %params = @_;
+
+ $params{'finished_cb'}->();
+}
##
## Device Handling
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};
}
$self->{'scan_running'} = 1;
- $self->{'taperscan'}->scan(result_cb => sub {
+ my $_user_msg_fn = sub {
+ my %params = @_;
+ if (exists($params{'slot_result'})) {
+ if ($params{'does_not_match_labelstr'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} with label $params{'label'} do not match labelstr");
+ } elsif ($params{'not_in_tapelist'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} with label $params{'label'} is not in the tapelist");
+ } elsif ($params{'active'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} with label $params{'label'} is not reusable");
+ } elsif ($params{'not_autolabel'}) {
+ if ($params{'label'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} with label $params{'label'} is not labelable ");
+ } 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, autolabel disabled");
+ } elsif ($params{'non_amanda'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ 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'}, autolabel disabled");
+ } elsif ($params{'not_success'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ 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 ($params{'not_labelable'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} without label can't be labeled");
+ } elsif (!defined $params{'label'}) {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} without label can be labeled");
+ } else {
+ $self->{'feedback'}->scribe_notif_log_info(
+ message => "Slot $params{'slot'} with label $params{'label'} is usable");
+ }
+ }
+ };
+
+ $self->{'taperscan'}->scan(
+ user_msg_fn => $_user_msg_fn,
+ result_cb => sub {
my ($error, $reservation, $volume_label, $access_mode, $is_new) = @_;
$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();
});