| Line 41... |
Line 41... |
| 41 |
use strict;
|
41 |
use strict;
|
| 42 |
use warnings;
|
42 |
use warnings;
|
| 43 |
|
43 |
|
| 44 |
our $VERSION = '0.1';
|
44 |
our $VERSION = '0.1';
|
| 45 |
|
45 |
|
| - |
|
46 |
use File::Basename;
|
| 46 |
use FindBin;
|
47 |
use FindBin;
|
| 47 |
use lib "$FindBin::Bin/..";
|
48 |
use lib "$FindBin::Bin/..";
|
| 48 |
use Data::Dumper;
|
49 |
use Data::Dumper;
|
| 49 |
use ZFS_Utils qw(loadConfig shredFile logMsg makeReplicateCommands mountDriveByLabel unmountDriveByLabel mountGeli runCmd sendReport fatalError cleanDirectory $logFileName $displayLogsOnConsole);
|
50 |
use ZFS_Utils qw(loadConfig shredFile logMsg makeReplicateCommands mountDriveByLabel unmountDriveByLabel mountGeli runCmd sendReport fatalError getDirectoryList cleanDirectory $logFileName $displayLogsOnConsole);
|
| 50 |
use Getopt::Long qw(GetOptions);
|
51 |
use Getopt::Long qw(GetOptions);
|
| 51 |
Getopt::Long::Configure ("bundling");
|
52 |
Getopt::Long::Configure ("bundling");
|
| 52 |
|
53 |
|
| 53 |
my $scriptDirectory = $FindBin::RealBin;
|
54 |
my $scriptDirectory = $FindBin::RealBin;
|
| 54 |
my $scriptFullPath = "$scriptDirectory/" . $FindBin::Script;
|
55 |
my $scriptFullPath = "$scriptDirectory/" . $FindBin::Script;
|
| Line 183... |
Line 184... |
| 183 |
}
|
184 |
}
|
| 184 |
}
|
185 |
}
|
| 185 |
|
186 |
|
| 186 |
# simple sub to take root/dataset/datset/dataset and turn it into
|
187 |
# simple sub to take root/dataset/datset/dataset and turn it into
|
| 187 |
# dataset.dataset.dataset
|
188 |
# dataset.dataset.dataset
|
| 188 |
sub replaceSlashWithDot {
|
189 |
sub dirnameToFileName {
|
| 189 |
my $string = shift;
|
190 |
my ( $string, $delimiter, $substitution ) = @_;
|
| 190 |
my @parts = split( "/", $string );
|
191 |
$delimiter //= '/';
|
| 191 |
shift @parts;
|
192 |
$substitution //= '.';
|
| - |
|
193 |
my @parts = split( /\Q$delimiter\E/, $string );
|
| 192 |
return join( '.', @parts );
|
194 |
return join( $substitution, @parts );
|
| 193 |
}
|
195 |
}
|
| 194 |
|
196 |
|
| 195 |
# perform replication on source server
|
197 |
# perform replication on source server
|
| 196 |
# $config - configuration hashref
|
198 |
# $config - configuration hashref
|
| 197 |
# $statusList - list of last snapshots replicated for each dataset in previous replications
|
199 |
# $statusList - list of last snapshots replicated for each dataset in previous replications
|
| Line 202... |
Line 204... |
| 202 |
my $newStatus = [];
|
204 |
my $newStatus = [];
|
| 203 |
foreach my $dataset ( sort keys %{$config->{datasets}} ) {
|
205 |
foreach my $dataset ( sort keys %{$config->{datasets}} ) {
|
| 204 |
logMsg("Processing dataset '$dataset'");
|
206 |
logMsg("Processing dataset '$dataset'");
|
| 205 |
# get list of all snapshots on dataset
|
207 |
# get list of all snapshots on dataset
|
| 206 |
my $sourceList;
|
208 |
my $sourceList;
|
| - |
|
209 |
# print Dumper( $config ) . "\n";
|
| - |
|
210 |
# print "$dataset\n";
|
| - |
|
211 |
# print Dumper( $config->{datasets}->{$dataset} ) . "\n";
|
| - |
|
212 |
# die;
|
| 207 |
if ( -e "$scriptDirectory/test.status") {
|
213 |
if ( -e "$scriptDirectory/test.status") {
|
| 208 |
$sourceList = getStatusFile( "$scriptDirectory/test.status" );
|
214 |
$sourceList = getStatusFile( "$scriptDirectory/test.status" );
|
| 209 |
} else {
|
215 |
} else {
|
| 210 |
$sourceList = [ runCmd( "zfs list -rt snap -H -o name $config->{datasets}-{$dataset}->{source}" ) ];
|
216 |
$sourceList = [ runCmd( "zfs list -rt snap -H -o name $config->{datasets}->{$dataset}->{source}" ) ];
|
| 211 |
}
|
217 |
}
|
| 212 |
|
218 |
|
| 213 |
# remove the parent part, leave the dataset itself
|
- |
|
| 214 |
$sourceList =~ s|/||;
|
- |
|
| 215 |
# process dataset here
|
219 |
# process dataset here
|
| 216 |
my $commands = makeReplicateCommands(
|
220 |
my $commands = makeReplicateCommands(
|
| 217 |
$sourceList,
|
221 |
$sourceList,
|
| 218 |
$statusList,
|
222 |
$statusList,
|
| 219 |
$dataset,
|
223 |
$dataset,
|
| Line 225... |
Line 229... |
| 225 |
foreach my $cmd ( keys %$commands ) {
|
229 |
foreach my $cmd ( keys %$commands ) {
|
| 226 |
my $command = $commands->{$cmd};
|
230 |
my $command = $commands->{$cmd};
|
| 227 |
my $outputFile = $cmd;
|
231 |
my $outputFile = $cmd;
|
| 228 |
$outputFile = replaceSlashWithDot($outputFile);
|
232 |
$outputFile = replaceSlashWithDot($outputFile);
|
| 229 |
$command .= " | openssl enc -aes-256-cbc -K $config->{transport}->{encryption}->{key} -iv $config->{transport}->{encryption}->{IV} " if $config->{transport}->{encryption}->{key};
|
233 |
$command .= " | openssl enc -aes-256-cbc -K $config->{transport}->{encryption}->{key} -iv $config->{transport}->{encryption}->{IV} " if $config->{transport}->{encryption}->{key};
|
| 230 |
$command .= " > $config->{transport}->{mount_point}/" . $cmd;
|
234 |
$command .= " > $config->{transport}->{mount_point}/" . dirnameToFileName( $cmd );
|
| 231 |
logMsg("Running command: $command");
|
235 |
logMsg("Running command: $command");
|
| 232 |
runCmd( $command ) unless $config->{dryrun};
|
236 |
runCmd( $command ) unless $config->{dryrun};
|
| 233 |
}
|
237 |
}
|
| 234 |
} else {
|
238 |
} else {
|
| 235 |
logMsg( "Nothing to do for $dataset" );
|
239 |
logMsg( "Nothing to do for $dataset" );
|
| Line 265... |
Line 269... |
| 265 |
# update the target datasets from the files on the transport drive
|
269 |
# update the target datasets from the files on the transport drive
|
| 266 |
sub updateTarget {
|
270 |
sub updateTarget {
|
| 267 |
my $config = shift;
|
271 |
my $config = shift;
|
| 268 |
my $files = getDirectoryList( $config->{transport}->{mount_point});
|
272 |
my $files = getDirectoryList( $config->{transport}->{mount_point});
|
| 269 |
foreach my $filename ( @$files ) {
|
273 |
foreach my $filename ( @$files ) {
|
| - |
|
274 |
my $targetDataset = basename( $filename );
|
| - |
|
275 |
my ($dataset) = split( /\Q\.\E/, $targetDataset ); # grab only the first element of a string which has internal delimiters
|
| 270 |
my $command = "cat $config->{output} | openssl enc -aes-256-cbc -d -K $config->{key} -iv $config->{IV}";
|
276 |
$targetDataset = $config->{datasets}->{$dataset}->{target} . '/' . dirnameToFileName( $targetDataset, '.', '/' );
|
| - |
|
277 |
my $command = "cat $filename";
|
| - |
|
278 |
$command .= " | openssl enc -aes-256-cbc -d -K $config->{transport}->{encryption}->{key} -iv $config->{transport}->{encryption}->{IV} " if $config->{transport}->{encryption}->{key};
|
| - |
|
279 |
$command .= " | zfs receive -F $targetDataset";
|
| - |
|
280 |
logMsg( $command );
|
| - |
|
281 |
runCmd( $command );
|
| 271 |
}
|
282 |
}
|
| 272 |
}
|
283 |
}
|
| 273 |
|
284 |
|
| 274 |
##################### main program starts here #####################
|
285 |
##################### main program starts here #####################
|
| 275 |
# Example to create a random key for encryption/decryption:
|
286 |
# Example to create a random key for encryption/decryption:
|
| Line 315... |
Line 326... |
| 315 |
$servername eq $config->{target}->{hostname} ? 'target' : 'unknown';
|
326 |
$servername eq $config->{target}->{hostname} ? 'target' : 'unknown';
|
| 316 |
|
327 |
|
| 317 |
#cleanup( $config, "Testing" );
|
328 |
#cleanup( $config, "Testing" );
|
| 318 |
|
329 |
|
| 319 |
# mount the transport drive, fatal error if we can not find it
|
330 |
# mount the transport drive, fatal error if we can not find it
|
| 320 |
fatalError( "Unable to mount tranport drive with label $config->{transport}->{disk_label}", $config, \&cleanup )
|
331 |
fatalError( "Unable to mount tranport drive with label $config->{transport}->{label}", $config, \&cleanup )
|
| 321 |
unless $config->{dryrun} or $config->{transport}->{mount_point} = mountDriveByLabel( $config->{transport} );
|
332 |
unless $config->{transport}->{mount_point} = mountDriveByLabel( $config->{transport} );
|
| 322 |
|
333 |
|
| 323 |
# main program logic
|
334 |
# main program logic
|
| 324 |
if ( $config->{runningAs} eq 'source' ) {
|
335 |
if ( $config->{runningAs} eq 'source' ) {
|
| 325 |
logMsg "Running as source server";
|
336 |
logMsg "Running as source server";
|
| 326 |
# remove all files from transport disk, but leave all subdirectories alone
|
337 |
# remove all files from transport disk, but leave all subdirectories alone
|
| Line 330... |
Line 341... |
| 330 |
$statusList = doSourceReplication($config, $statusList);
|
341 |
$statusList = doSourceReplication($config, $statusList);
|
| 331 |
writeStatusFile($config->{status_file}, $statusList) unless $config->{dryrun};
|
342 |
writeStatusFile($config->{status_file}, $statusList) unless $config->{dryrun};
|
| 332 |
} elsif ( $config->{runningAs} eq 'target' ) {
|
343 |
} elsif ( $config->{runningAs} eq 'target' ) {
|
| 333 |
logMsg "Running as target server";
|
344 |
logMsg "Running as target server";
|
| 334 |
mountGeli( $config->{target}->{geli} ) if ( defined $config->{target}->{geli} );
|
345 |
mountGeli( $config->{target}->{geli} ) if ( defined $config->{target}->{geli} );
|
| - |
|
346 |
umountDiskByLabel( $config->{target}->{geli}->{secureKey} )
|
| - |
|
347 |
unless $config->{target}->{geli}->{secureKey}->{label} eq $config->{transport}->{label};
|
| - |
|
348 |
print "Please insert device labeled REPORT\n" if $config->{target}->{report}->{targetDrive}->{label};
|
| 335 |
updateTarget( $config );
|
349 |
updateTarget( $config );
|
| 336 |
} else {
|
350 |
} else {
|
| 337 |
fatalError( "This server ($servername) is neither source nor target server as per config\n" );
|
351 |
fatalError( "This server ($servername) is neither source nor target server as per config\n" );
|
| 338 |
}
|
352 |
}
|
| 339 |
|
353 |
|