1 # Copyright (c) 2009-2012 Zmanda Inc. All Rights Reserved.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
22 Amanda::Taper::Controller
26 This package is a component of the Amanda taper, and is not intended for use by
27 other scripts or applications.
29 The controller interfaces with the driver (via L<Amanda::Taper::Protocol>) and
30 controls one or more workers (L<Amanda::Taper::Worker>).
32 The controller create an L<Amanda::Taper::Worker> object for each
33 START_TAPER command it receive. It dispatch the following commands
34 to the correct worker.
38 use lib '@amperldir@';
42 package Amanda::Taper::Controller;
44 use POSIX qw( :errno_h );
46 use Amanda::Config qw( :getconf config_dir_relative );
49 use Amanda::MainLoop qw( :GIOCondition );
51 use Amanda::Taper::Protocol;
52 use Amanda::Taper::Scan;
53 use Amanda::Taper::Worker;
54 use Amanda::Interactivity;
55 use Amanda::Logfile qw( :logtype_t log_add );
56 use Amanda::Xfer qw( :constants );
57 use Amanda::Util qw( quote_string );
69 tapelist => $params{'tapelist'},
76 # The feedback object mediates between messages from the driver and the ongoing
77 # action with the taper. This is made a little bit complicated because the
78 # driver conversation is fairly contextual, with some responses answering
79 # "questions" asked earlier. This is modeled with the following taper
83 # waiting for START-TAPER command
85 # warming up devices; TAPER-OK not sent yet
87 # not currently dumping anything
89 # setting up a transfer for a new dump
91 # getting the header before beginning a new dump
93 # in the middle of writing a file (self->{'handle'} set)
95 # a fatal error has occurred, so this object won't do anything
100 my $message_cb = make_cb(message_cb => sub {
101 my ($msgtype, %params) = @_;
103 if (defined $msgtype) {
104 $msg = "unhandled command '$msgtype'";
106 $msg = $params{'error'};
108 log_add($L_ERROR, "$msg");
109 print STDERR "$msg\n";
110 $self->{'proto'}->send(Amanda::Taper::Protocol::BAD_COMMAND,
113 $self->{'proto'} = Amanda::Taper::Protocol->new(
116 message_cb => $message_cb,
117 message_obj => $self,
118 debug => $Amanda::Config::debug_taper?'taper/driver':'',
121 my $changer = Amanda::Changer->new(undef, tapelist => $self->{'tapelist'});
122 if ($changer->isa("Amanda::Changer::Error")) {
123 # send a TAPE_ERROR right away
124 $self->{'proto'}->send(Amanda::Taper::Protocol::TAPE_ERROR,
125 worker_name => "SETUP",
126 message => "$changer");
128 # log the error (note that the message is intentionally not quoted)
129 log_add($L_ERROR, "no-tape error [$changer]");
131 # wait for it to be transmitted, then exit
132 $self->{'proto'}->stop(finished_cb => sub {
133 Amanda::MainLoop::quit();
136 # don't finish start()ing
140 my $interactivity = Amanda::Interactivity->new(
141 name => getconf($CNF_INTERACTIVITY));
142 my $scan_name = getconf($CNF_TAPERSCAN);
143 $self->{'taperscan'} = Amanda::Taper::Scan->new(algorithm => $scan_name,
145 interactivity => $interactivity,
146 tapelist => $self->{'tapelist'});
155 my $steps = define_steps
156 cb_ref => \$params{'finished_cb'};
159 @worker = values %{$self->{'worker'}};
160 delete $self->{'worker'};
161 $steps->{'quit_scribe'}->();
164 step quit_scribe => sub {
165 my $worker = shift @worker;
166 if (defined $worker and defined $worker->{'scribe'}) {
167 $worker->{'scribe'}->quit(finished_cb => sub {
169 push @errors, $err if ($err);
171 $steps->{'quit_scribe'}->();
174 $steps->{'stop_proto'}->();
178 step stop_proto => sub {
179 $self->{'proto'}->stop(finished_cb => sub {
181 push @errors, $err if ($err);
183 $steps->{'done'}->();
188 $self->{'taperscan'}->quit() if defined $self->{'taperscan'};
190 $params{'finished_cb'}->(join("; ", @errors));
192 $params{'finished_cb'}->();
200 sub msg_START_TAPER {
202 my ($msgtype, %params) = @_;
204 my $worker = new Amanda::Taper::Worker($params{'worker_name'}, $self,
205 $params{'timestamp'});
207 $self->{'worker'}->{$params{'worker_name'}} = $worker;
209 $self->{'timestamp'} = $params{'timestamp'};
212 # defer both PORT_ and FILE_WRITE to a common method
215 my ($msgtype, %params) = @_;
217 my $worker = $self->{'worker'}->{$params{'worker_name'}};
218 $worker->FILE_WRITE(@_);
223 my ($msgtype, %params) = @_;
225 my $worker = $self->{'worker'}->{$params{'worker_name'}};
226 $worker->PORT_WRITE(@_);
231 my ($msgtype, %params) = @_;
233 my $worker = $self->{'worker'}->{$params{'worker_name'}};
234 $worker->START_SCAN(@_);
239 my ($msgtype, %params) = @_;
241 my $worker = $self->{'worker'}->{$params{'worker_name'}};
242 $worker->NEW_TAPE(@_);
245 sub msg_NO_NEW_TAPE {
247 my ($msgtype, %params) = @_;
249 my $worker = $self->{'worker'}->{$params{'worker_name'}};
250 $worker->NO_NEW_TAPE(@_);
255 my ($msgtype, %params) = @_;
257 my $worker = $self->{'worker'}->{$params{'worker_name'}};
263 my ($msgtype, %params) = @_;
265 my $worker = $self->{'worker'}->{$params{'worker_name'}};
269 sub msg_CLOSE_VOLUME {
271 my ($msgtype, %params) = @_;
273 my $worker = $self->{'worker'}->{$params{'worker_name'}};
274 $worker->CLOSE_VOLUME(@_);
277 sub msg_TAKE_SCRIBE_FROM {
279 my ($msgtype, %params) = @_;
281 my $worker = $self->{'worker'}->{$params{'worker_name'}};
282 my $worker1 = $self->{'worker'}->{$params{'from_worker_name'}};
283 $worker->TAKE_SCRIBE_FROM($worker1, @_);
284 delete $self->{'worker'}->{$params{'from_worker_name'}};
289 my ($msgtype, %params) = @_;
292 # because the driver hangs up on us immediately after sending QUIT,
293 # and EOF also means QUIT, we tend to get this command repeatedly.
294 # So check to make sure this is only called once
295 return if $self->{'quitting'};
296 $self->{'quitting'} = 1;
298 my $finished_cb = make_cb(finished_cb => sub {
301 Amanda::Debug::debug("Quit error: $err");
303 Amanda::MainLoop::quit();
305 $self->quit(finished_cb => $finished_cb);