| Line 8... |
Line 8... |
| 8 |
use Data::Dumper;
|
8 |
use Data::Dumper;
|
| 9 |
use ZFS_Utils qw(loadConfig shredFile logMsg makeReplicateCommands mountDriveByLabel mountGeli runCmd $logFileName $displayLogsOnConsole);
|
9 |
use ZFS_Utils qw(loadConfig shredFile logMsg makeReplicateCommands mountDriveByLabel mountGeli runCmd $logFileName $displayLogsOnConsole);
|
| 10 |
|
10 |
|
| 11 |
# set the log file to be next to this script
|
11 |
# set the log file to be next to this script
|
| 12 |
$logFileName = "$FindBin::Bin/sneakernet.log";
|
12 |
$logFileName = "$FindBin::Bin/sneakernet.log";
|
| - |
|
13 |
# log only for one run
|
| - |
|
14 |
unlink ( $logFileName ) if -f $logFileName;
|
| - |
|
15 |
|
| 13 |
# display all log messages on console in addition to the log file
|
16 |
# display all log messages on console in addition to the log file
|
| 14 |
$displayLogsOnConsole = 1;
|
17 |
$displayLogsOnConsole = 1;
|
| 15 |
|
18 |
|
| 16 |
my $configFileName = "$0.conf.yaml";
|
19 |
my $configFileName = "$0.conf.yaml";
|
| 17 |
|
20 |
|
| Line 105... |
Line 108... |
| 105 |
logMsg("Error: could not write status file '$filename': $!");
|
108 |
logMsg("Error: could not write status file '$filename': $!");
|
| 106 |
die;
|
109 |
die;
|
| 107 |
}
|
110 |
}
|
| 108 |
}
|
111 |
}
|
| 109 |
|
112 |
|
| - |
|
113 |
# simple sub to take root/dataset/datset/dataset and turn it into
|
| - |
|
114 |
# dataset.dataset.dataset
|
| - |
|
115 |
sub replaceSlashWithDot {
|
| - |
|
116 |
my $string = shift;
|
| - |
|
117 |
my @parts = split( "/", $string );
|
| - |
|
118 |
shift @parts;
|
| - |
|
119 |
return join( '.', @parts );
|
| - |
|
120 |
}
|
| - |
|
121 |
|
| 110 |
# perform replication on source server
|
122 |
# perform replication on source server
|
| 111 |
# $config - configuration hashref
|
123 |
# $config - configuration hashref
|
| 112 |
# $statusList - list of last snapshots replicated for each dataset in previous replications
|
124 |
# $statusList - list of last snapshots replicated for each dataset in previous replications
|
| 113 |
# return new status list after replication containing updated last snapshots
|
125 |
# return new status list after replication containing updated last snapshots
|
| 114 |
# this script will actually replicate the datasets to the sneakernet disk
|
126 |
# this script will actually replicate the datasets to the sneakernet disk
|
| 115 |
sub doSourceReplication {
|
127 |
sub doSourceReplication {
|
| 116 |
my ($config, $statusList) = @_;
|
128 |
my ($config, $statusList) = @_;
|
| 117 |
my $newStatus = [];
|
129 |
my $newStatus = [];
|
| 118 |
foreach my $dataset ( sort keys %{$config->{datasets}} ) {
|
130 |
foreach my $dataset ( sort keys %{$config->{datasets}} ) {
|
| 119 |
logMsg("Processing dataset '$dataset'\n");
|
131 |
logMsg("Processing dataset '$dataset'");
|
| - |
|
132 |
# get list of all snapshots on dataset
|
| 120 |
my $sourceList = [ runCmd( "zfs", "list", "-rt", "snap", "-H", "-o", "name", $config->{datasets}->{$dataset}->{source} ) ];
|
133 |
my $sourceList = [ runCmd( "zfs", "list", "-rt", "snap", "-H", "-o", "name", $config->{datasets}->{$dataset}->{source} ) ];
|
| 121 |
|
- |
|
| 122 |
# process dataset here
|
134 |
# process dataset here
|
| 123 |
my $commands = makeReplicateCommands($sourceList, $statusList, $newStatus );
|
135 |
my $commands = makeReplicateCommands($sourceList, $statusList, $newStatus );
|
| - |
|
136 |
if ( %$commands ) {
|
| 124 |
foreach my $cmd ( @$commands ) {
|
137 |
foreach my $cmd ( keys %$commands ) {
|
| - |
|
138 |
my $command = $commands->{$cmd};
|
| - |
|
139 |
$command .= " | openssl enc -aes-256-cbc -K $config->{transport}->{encryption}->{key} -iv $config->{transport}->{encryption}->{IV} " if $config->{transport}->{encryption}->{key};
|
| - |
|
140 |
$command .= " > $config->{transport}->{mount_point}/" . replaceSlashWithDot($cmd);
|
| 125 |
logMsg("Running command: $cmd\n");
|
141 |
logMsg("Running command: $command");
|
| 126 |
#runCmd( split( /\s+/, $cmd ) );
|
142 |
#runCmd( split( /\s+/, $cmd ) );
|
| - |
|
143 |
}
|
| - |
|
144 |
} else {
|
| - |
|
145 |
logMsg( "Nothing to do for $dataset" );
|
| 127 |
}
|
146 |
}
|
| 128 |
}
|
147 |
}
|
| 129 |
return $newStatus;
|
148 |
return $newStatus;
|
| 130 |
}
|
149 |
}
|
| 131 |
|
150 |
|
| - |
|
151 |
# how to handle a fatal error
|
| - |
|
152 |
sub fatalError {
|
| - |
|
153 |
my $message = shift;
|
| - |
|
154 |
logMsg( $message );
|
| - |
|
155 |
die;
|
| - |
|
156 |
}
|
| - |
|
157 |
|
| 132 |
|
158 |
|
| 133 |
##################### main program starts here #####################
|
159 |
##################### main program starts here #####################
|
| 134 |
# Example to create a random key for encryption/decryption:
|
160 |
# Example to create a random key for encryption/decryption:
|
| 135 |
# generate a random key with
|
161 |
# generate a random key with
|
| 136 |
# openssl rand 32 | xxd -p | tr -d '\n' > test.key
|
162 |
# openssl rand 32 | xxd -p | tr -d '\n' > test.key
|
| Line 140... |
Line 166... |
| 140 |
|
166 |
|
| 141 |
# set some defaults
|
167 |
# set some defaults
|
| 142 |
$config->{'status_file'} = "$0.status" unless ( defined $config->{'status_file'} );
|
168 |
$config->{'status_file'} = "$0.status" unless ( defined $config->{'status_file'} );
|
| 143 |
|
169 |
|
| 144 |
|
170 |
|
| 145 |
die "Invalid config file: missing source and/or target server\n"
|
171 |
fatalError( "Invalid config file: missing source and/or target server" )
|
| 146 |
unless (defined $config->{source_server} && defined $config->{target_server});
|
172 |
unless (defined $config->{source_server} && defined $config->{target_server});
|
| 147 |
|
173 |
|
| - |
|
174 |
# mount the transport drive, fatal error if we can not find it
|
| - |
|
175 |
fatalError( "Unable to mount tranport drive with label $config->{transport}->{disk_label}" )
|
| - |
|
176 |
unless $config->{transport}->{mount_point} = mountDriveByLabel( $config->{transport}->{disk_label}, $config->{transport}->{mount_point}, $config->{transport}->{timeout} );
|
| - |
|
177 |
|
| 148 |
my $servername = `hostname -s`;
|
178 |
my $servername = `hostname -s`;
|
| 149 |
chomp $servername;
|
179 |
chomp $servername;
|
| 150 |
if ( $servername eq $config->{source_server}->{hostname} ) {
|
180 |
if ( $servername eq $config->{source_server}->{hostname} ) {
|
| 151 |
logMsg "Running as source server\n";
|
181 |
logMsg "Running as source server\n";
|
| 152 |
my $statusList = getStatusFile($config->{status_file});
|
182 |
my $statusList = getStatusFile($config->{status_file});
|
| Line 160... |
Line 190... |
| 160 |
} else {
|
190 |
} else {
|
| 161 |
logMsg "This server ($servername) is neither source nor target server as per config\n";
|
191 |
logMsg "This server ($servername) is neither source nor target server as per config\n";
|
| 162 |
die;
|
192 |
die;
|
| 163 |
}
|
193 |
}
|
| 164 |
|
194 |
|
| - |
|
195 |
# unmount the sneakernet drive
|
| - |
|
196 |
`umount $config->{transport}->{mount_point}`;
|
| - |
|
197 |
# and remove the directory
|
| - |
|
198 |
unlink $config->{transport}->{mount_point};
|
| - |
|
199 |
|
| 165 |
die "Source and target server logic not yet implemented\n";
|
200 |
die "Source and target server logic not yet implemented\n";
|
| 166 |
|
201 |
|
| 167 |
#my $newStatus = [];
|
202 |
#my $newStatus = [];
|
| 168 |
#foreach my $dataset ( sort keys %{$config->{datasets}} ) {
|
203 |
#foreach my $dataset ( sort keys %{$config->{datasets}} ) {
|
| 169 |
# logMsg("Processing dataset '$dataset'");
|
204 |
# logMsg("Processing dataset '$dataset'");
|