1 # Copyright (c) 2009, 2010 Zmanda Inc. All Rights Reserved.
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
21 Amanda::Taper::Controller
25 This package is a component of the Amanda taper, and is not intended for use by
26 other scripts or applications.
28 The controller interfaces with the driver (via L<Amanda::Taper::Protocol>) and
29 controls one or more workers (L<Amanda::Taper::Worker>).
31 The controller create an L<Amanda::Taper::Worker> object for each
32 START_TAPER command it receive. It dispatch the following commands
33 to the correct worker.
37 use lib '@amperldir@';
41 package Amanda::Taper::Controller;
43 use POSIX qw( :errno_h );
45 use Amanda::Config qw( :getconf config_dir_relative );
48 use Amanda::MainLoop qw( :GIOCondition );
50 use Amanda::Taper::Protocol;
51 use Amanda::Taper::Scan;
52 use Amanda::Taper::Worker;
53 use Amanda::Interactivity;
54 use Amanda::Logfile qw( :logtype_t log_add );
55 use Amanda::Xfer qw( :constants );
56 use Amanda::Util qw( quote_string );
68 tapelist => $params{'tapelist'},
75 # The feedback object mediates between messages from the driver and the ongoing
76 # action with the taper. This is made a little bit complicated because the
77 # driver conversation is fairly contextual, with some responses answering
78 # "questions" asked earlier. This is modeled with the following taper
82 # waiting for START-TAPER command
84 # warming up devices; TAPER-OK not sent yet
86 # not currently dumping anything
88 # setting up a transfer for a new dump
90 # getting the header before beginning a new dump
92 # in the middle of writing a file (self->{'handle'} set)
94 # a fatal error has occurred, so this object won't do anything
99 my $message_cb = make_cb(message_cb => sub {
100 my ($msgtype, %params) = @_;
102 if (defined $msgtype) {
103 $msg = "unhandled command '$msgtype'";
105 $msg = $params{'error'};
107 log_add($L_ERROR, "$msg");
108 print STDERR "$msg\n";
109 $self->{'proto'}->send(Amanda::Taper::Protocol::BAD_COMMAND,
112 $self->{'proto'} = Amanda::Taper::Protocol->new(
115 message_cb => $message_cb,
116 message_obj => $self,
117 debug => $Amanda::Config::debug_taper?'driver/taper':'',
120 my $changer = Amanda::Changer->new(undef, tapelist => $self->{'tapelist'});
121 if ($changer->isa("Amanda::Changer::Error")) {
122 # send a TAPE_ERROR right away
123 $self->{'proto'}->send(Amanda::Taper::Protocol::TAPE_ERROR,
124 worker_name => "SETUP",
125 message => "$changer");
127 # log the error (note that the message is intentionally not quoted)
128 log_add($L_ERROR, "no-tape error [$changer]");
130 # wait for it to be transmitted, then exit
131 $self->{'proto'}->stop(finished_cb => sub {
132 Amanda::MainLoop::quit();
135 # don't finish start()ing
139 my $interactivity = Amanda::Interactivity->new(
140 name => getconf($CNF_INTERACTIVITY));
141 my $scan_name = getconf($CNF_TAPERSCAN);
142 $self->{'taperscan'} = Amanda::Taper::Scan->new(algorithm => $scan_name,
144 interactivity => $interactivity,
145 tapelist => $self->{'tapelist'});
154 my $steps = define_steps
155 cb_ref => \$params{'finished_cb'};
158 @worker = values %{$self->{'worker'}};
159 delete $self->{'worker'};
160 $steps->{'quit_scribe'}->();
163 step quit_scribe => sub {
164 my $worker = shift @worker;
165 if (defined $worker and defined $worker->{'scribe'}) {
166 $worker->{'scribe'}->quit(finished_cb => sub {
168 push @errors, $err if ($err);
170 $steps->{'quit_scribe'}->();
173 $steps->{'stop_proto'}->();
177 step stop_proto => sub {
178 $self->{'proto'}->stop(finished_cb => sub {
180 push @errors, $err if ($err);
182 $steps->{'done'}->();
187 $self->{'taperscan'}->quit() if defined $self->{'taperscan'};
189 $params{'finished_cb'}->(join("; ", @errors));
191 $params{'finished_cb'}->();
199 sub msg_START_TAPER {
201 my ($msgtype, %params) = @_;
203 my $worker = new Amanda::Taper::Worker($params{'worker_name'}, $self,
204 $params{'timestamp'});
206 $self->{'worker'}->{$params{'worker_name'}} = $worker;
208 $self->{'timestamp'} = $params{'timestamp'};
211 # defer both PORT_ and FILE_WRITE to a common method
214 my ($msgtype, %params) = @_;
216 my $worker = $self->{'worker'}->{$params{'worker_name'}};
217 $worker->FILE_WRITE(@_);
222 my ($msgtype, %params) = @_;
224 my $worker = $self->{'worker'}->{$params{'worker_name'}};
225 $worker->PORT_WRITE(@_);
230 my ($msgtype, %params) = @_;
232 my $worker = $self->{'worker'}->{$params{'worker_name'}};
233 $worker->START_SCAN(@_);
238 my ($msgtype, %params) = @_;
240 my $worker = $self->{'worker'}->{$params{'worker_name'}};
241 $worker->NEW_TAPE(@_);
244 sub msg_NO_NEW_TAPE {
246 my ($msgtype, %params) = @_;
248 my $worker = $self->{'worker'}->{$params{'worker_name'}};
249 $worker->NO_NEW_TAPE(@_);
254 my ($msgtype, %params) = @_;
256 my $worker = $self->{'worker'}->{$params{'worker_name'}};
262 my ($msgtype, %params) = @_;
264 my $worker = $self->{'worker'}->{$params{'worker_name'}};
268 sub msg_CLOSE_VOLUME {
270 my ($msgtype, %params) = @_;
272 my $worker = $self->{'worker'}->{$params{'worker_name'}};
273 $worker->CLOSE_VOLUME(@_);
276 sub msg_TAKE_SCRIBE_FROM {
278 my ($msgtype, %params) = @_;
280 my $worker = $self->{'worker'}->{$params{'worker_name'}};
281 my $worker1 = $self->{'worker'}->{$params{'from_worker_name'}};
282 $worker->TAKE_SCRIBE_FROM($worker1, @_);
283 delete $self->{'worker'}->{$params{'from_worker_name'}};
288 my ($msgtype, %params) = @_;
291 # because the driver hangs up on us immediately after sending QUIT,
292 # and EOF also means QUIT, we tend to get this command repeatedly.
293 # So check to make sure this is only called once
294 return if $self->{'quitting'};
295 $self->{'quitting'} = 1;
297 my $finished_cb = make_cb(finished_cb => sub {
300 Amanda::Debug::debug("Quit error: $err");
302 Amanda::MainLoop::quit();
304 $self->quit(finished_cb => $finished_cb);