628f3aff96a7e9b5d876f32da678cb298cb41461
[debian/amanda] / application-src / amzfs-sendrecv.pl
1 #!@PERL@
2 # Copyright (c) 2005-2008 Zmanda Inc.  All Rights Reserved.
3 #
4 # This program is free software; you can redistribute it and/or modify it
5 # under the terms of the GNU General Public License version 2 as published
6 # by the Free Software Foundation.
7 #
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
11 # for more details.
12 #
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
16 #
17 # Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19
20 use lib '@amperldir@';
21 use strict;
22 use Getopt::Long;
23
24 package Amanda::Application::Amzfs_sendrecv;
25 use base qw(Amanda::Application Amanda::Application::Zfs);
26 use File::Copy;
27 use File::Path;
28 use IPC::Open3;
29 use Sys::Hostname;
30 use Symbol;
31 use Amanda::Constants;
32 use Amanda::Config qw( :init :getconf  config_dir_relative );
33 use Amanda::Debug qw( :logging );
34 use Amanda::Paths;
35 use Amanda::Util qw( :constants );
36
37 sub new {
38     my $class = shift;
39     my ($config, $host, $disk, $device, $level, $index, $message, $collection, $record, $df_path, $zfs_path, $pfexec_path, $pfexec, $keep_snapshot) = @_;
40     my $self = $class->SUPER::new();
41
42     $self->{config}     = $config;
43     $self->{host}       = $host;
44     $self->{disk}       = $disk;
45     $self->{device}     = $device;
46     $self->{level}      = [ @{$level} ];
47     $self->{index}      = $index;
48     $self->{message}    = $message;
49     $self->{collection} = $collection;
50     $self->{record}     = $record;
51     $self->{df_path}       = $df_path;
52     $self->{zfs_path}      = $zfs_path;
53     $self->{pfexec_path}   = $pfexec_path;
54     $self->{pfexec}        = $pfexec;
55     $self->{keep_snapshot} = $keep_snapshot;
56     $self->{pfexec_cmd}    = undef;
57
58     if ($self->{keep_snapshot} =~ /^YES$/i) {
59         $self->{keep_snapshot} = "YES";
60         if (!defined $self->{record}) {
61             $self->{keep_snapshot} = "NO";
62         }
63     }
64
65     return $self;
66 }
67
68 sub check_for_backup_failure {
69    my $self = shift;
70    my $action = shift;
71
72    $self->zfs_destroy_snapshot($action);
73 }
74
75 sub command_support {
76    my $self = shift;
77
78    print "CONFIG YES\n";
79    print "HOST YES\n";
80    print "DISK YES\n";
81    print "MAX-LEVEL 9\n";
82    print "INDEX-LINE NO\n";
83    print "INDEX-XML NO\n";
84    print "MESSAGE-LINE YES\n";
85    print "MESSAGE-XML NO\n";
86    print "RECORD YES\n";
87    print "COLLECTION NO\n";
88 }
89
90 sub command_selfcheck {
91     my $self = shift;
92
93     $self->zfs_set_value("check");
94     if ($self->{error_status} == $Amanda::Script_App::GOOD) {
95         $self->zfs_create_snapshot("check");
96         $self->zfs_destroy_snapshot("check");
97         print "OK " . $self->{disk} . "\n";
98         print "OK " . $self->{device} . "\n";
99     }
100 }
101
102 sub command_estimate() {
103     my $self = shift;
104
105     my $level = 0;
106
107     $self->zfs_set_value("estimate");
108     $self->zfs_create_snapshot("estimate");
109
110     while (defined ($level = shift @{$self->{level}})) {
111       debug "Estimate of level $level";
112       my $size = $self->estimate_snapshot($level);
113       output_size($level, $size);
114     }
115
116     $self->zfs_destroy_snapshot("estimate");
117
118     exit 0;
119 }
120
121 sub output_size {
122    my($level) = shift;
123    my($size) = shift;
124    if($size == -1) {
125       print "$level -1 -1\n";
126       #exit 2;
127    }
128    else {
129       my($ksize) = int $size / (1024);
130       $ksize=32 if ($ksize<32);
131       print "$level $ksize 1\n";
132    }
133 }
134
135 sub command_backup {
136     my $self = shift;
137
138     my $mesgout_fd;
139     open($mesgout_fd, '>&=3') || die();
140     $self->{mesgout} = $mesgout_fd;
141
142     $self->zfs_set_value("backup");
143     $self->zfs_create_snapshot("backup");
144
145     my $size = -1;
146     my $level = $self->{level}[0];
147     my $cmd;
148     debug "Backup of level $level";
149     if ($level == 0) {
150        $cmd = "$self->{pfexec_cmd} $self->{zfs_path} send $self->{filesystem}\@$self->{snapshot} | $Amanda::Paths::amlibexecdir/teecount";
151     } else {
152       my $refsnapshotname = $self->zfs_find_snapshot_level($level-1);
153       debug "Referenced snapshot name: $refsnapshotname|";
154       if ($refsnapshotname ne "") {
155         $cmd = "$self->{pfexec_cmd} $self->{zfs_path} send -i $refsnapshotname $self->{filesystem}\@$self->{snapshot} | $Amanda::Paths::amlibexecdir/teecount";
156       } else {
157         $self->print_to_server_and_die("sendbackup", "cannot backup snapshot '$self->{filesystem}\@$self->{snapshot}': reference snapshot doesn't exists for level $level", $Amanda::Script_App::ERROR);
158       }
159     }
160
161     debug "running (backup): $cmd";
162     my($wtr, $err, $pid);
163     my($errmsg);
164     $err = Symbol::gensym;
165     $pid = open3($wtr, '>&STDOUT', $err, $cmd);
166     close $wtr;
167     $errmsg = <$err>;
168     waitpid $pid, 0;
169     close $err;
170     if ($? !=  0) {
171         if (defined $errmsg) {
172             $self->print_to_server_and_die("sendbackup", $errmsg, $Amanda::Script_App::ERROR);
173         } else {
174             $self->print_to_server_and_die("sendbackup", "cannot backup snapshot '$self->{filesystem}\@$self->{snapshot}': unknown reason", $Amanda::Script_App::ERROR);
175         }
176     }
177     $size = $errmsg;
178     debug "Dump done";
179
180     my($ksize) = int ($size/1024);
181     $ksize=32 if ($ksize<32);
182
183     print $mesgout_fd "sendbackup: size $ksize\n";
184     print $mesgout_fd "sendbackup: end\n";
185
186     # destroy all snapshot of this level and higher
187     $self->zfs_purge_snapshot($level, 9, "backup");
188
189     if ($self->{keep_snapshot} eq 'YES') {
190         $self->zfs_rename_snapshot($level, "backup");
191     } else {
192         $self->zfs_destroy_snapshot("backup");
193     }
194
195     exit 0;
196 }
197
198 sub estimate_snapshot
199 {
200     my $self = shift;
201     my $level = shift;
202     my $action = shift;
203
204     debug "\$filesystem = $self->{filesystem}";
205     debug "\$snapshot = $self->{snapshot}";
206     debug "\$level = $level";
207
208     my $cmd;
209     if ($level == 0) {
210       $cmd = "$self->{pfexec_cmd} $self->{zfs_path} get -Hp -o value referenced $self->{filesystem}\@$self->{snapshot}";
211     } else {
212       my $refsnapshotname = $self->zfs_find_snapshot_level($level-1);
213       debug "Referenced snapshot name: $refsnapshotname|";
214       if ($refsnapshotname ne "") {
215         $cmd = "$self->{pfexec_cmd} $self->{zfs_path} send -i $refsnapshotname $self->{filesystem}\@$self->{snapshot} | /usr/bin/wc -c";
216       } else {
217         return "-1";
218       }
219     }
220     debug "running (estimate): $cmd";
221     my($wtr, $rdr, $err, $pid);
222     $err = Symbol::gensym;
223     $pid = open3($wtr, $rdr, $err, $cmd);
224     close $wtr;
225     my ($msg) = <$rdr>;
226     my ($errmsg) = <$err>;
227     waitpid $pid, 0;
228     close $rdr;
229     close $err;
230     if ($? !=  0) {
231         if (defined $msg && defined $errmsg) {
232             $self->print_to_server_and_die($action, "$msg, $errmsg", $Amanda::Script_App::ERROR);
233         } elsif (defined $msg) {
234             $self->print_to_server_and_die($action, $msg, $Amanda::Script_App::ERROR);
235         } elsif (defined $errmsg) {
236             $self->print_to_server_and_die($action, $errmsg, $Amanda::Script_App::ERROR);
237         } else {
238                 $self->print_to_server_and_die($action, "cannot estimate snapshot '$self->{snapshot}\@$self->{snapshot}': unknown reason", $Amanda::Script_App::ERROR);
239         }
240     }
241
242     return $msg;
243 }
244
245 sub command_index_from_output {
246 }
247
248 sub command_index_from_image {
249 }
250
251 sub command_restore {
252 }
253
254 sub command_print_command {
255 }
256
257 package main;
258
259 sub usage {
260     print <<EOF;
261 Usage: amzfs-sendrecv <command> --config=<config> --host=<host> --disk=<disk> --device=<device> --level=<level> --index=<yes|no> --message=<text> --collection=<no> --record=<yes|no> --df-path=<path/to/df> --zfs-path=<path/to/zfs> --pfexec-path=<path/to/pfexec> --pfexec=<yes|no> --keep-snapshot=<yes|no>.
262 EOF
263     exit(1);
264 }
265
266 my $opt_config;
267 my $opt_host;
268 my $opt_disk;
269 my $opt_device;
270 my @opt_level;
271 my $opt_index;
272 my $opt_message;
273 my $opt_collection;
274 my $opt_record;
275 my $df_path  = 'df';
276 my $zfs_path = 'zfs';
277 my $pfexec_path = 'pfexec';
278 my $pfexec = "NO";
279 my $opt_keep_snapshot = "YES";
280
281 Getopt::Long::Configure(qw{bundling});
282 GetOptions(
283     'config=s'        => \$opt_config,
284     'host=s'          => \$opt_host,
285     'disk=s'          => \$opt_disk,
286     'device=s'        => \$opt_device,
287     'level=s'         => \@opt_level,
288     'index=s'         => \$opt_index,
289     'message=s'       => \$opt_message,
290     'collection=s'    => \$opt_collection,
291     'record'          => \$opt_record,
292     'df-path=s'       => \$df_path,
293     'zfs-path=s'      => \$zfs_path,
294     'pfexec-path=s'   => \$pfexec_path,
295     'pfexec=s'        => \$pfexec,
296     'keep-snapshot=s' => \$opt_keep_snapshot
297 ) or usage();
298
299 my $application = Amanda::Application::Amzfs_sendrecv->new($opt_config, $opt_host, $opt_disk, $opt_device, \@opt_level, $opt_index, $opt_message, $opt_collection, $opt_record, $df_path, $zfs_path, $pfexec_path, $pfexec, $opt_keep_snapshot);
300
301 $application->do($ARGV[0]);
302