| Line 51... |
Line 51... |
| 51 |
my $scriptDirectory = $FindBin::RealBin;
|
51 |
my $scriptDirectory = $FindBin::RealBin;
|
| 52 |
my $scriptFullPath = "$scriptDirectory/" . $FindBin::Script;
|
52 |
my $scriptFullPath = "$scriptDirectory/" . $FindBin::Script;
|
| 53 |
|
53 |
|
| 54 |
|
54 |
|
| 55 |
# if set, will not actually write files to disk
|
55 |
# if set, will not actually write files to disk
|
| 56 |
my $DEBUG = 0;
|
56 |
my $DEBUG = 1;
|
| 57 |
|
57 |
|
| 58 |
# display all log messages on console in addition to the log file
|
58 |
# display all log messages on console in addition to the log file
|
| 59 |
$displayLogsOnConsole = 1;
|
59 |
$displayLogsOnConsole = 1;
|
| 60 |
|
60 |
|
| 61 |
my $configFileName = "$scriptFullPath.conf.yaml";
|
61 |
my $configFileName = "$scriptFullPath.conf.yaml";
|
| Line 63... |
Line 63... |
| 63 |
my $config = {
|
63 |
my $config = {
|
| 64 |
# file created on source server to track last copyed dataset
|
64 |
# file created on source server to track last copyed dataset
|
| 65 |
'status_file' => "$scriptFullPath.status",
|
65 |
'status_file' => "$scriptFullPath.status",
|
| 66 |
'log_file' => "$scriptFullPath.log",
|
66 |
'log_file' => "$scriptFullPath.log",
|
| 67 |
#information about source server
|
67 |
#information about source server
|
| 68 |
'source_server' => {
|
68 |
'source' => {
|
| 69 |
'hostname' => '', # used to see if we are on source
|
69 |
'hostname' => '', # used to see if we are on source
|
| 70 |
'poolname' => 'pool', # name of the ZFS pool to export
|
70 |
'poolname' => 'pool', # name of the ZFS pool to export
|
| 71 |
# if set, will generate a report via email or by storing on a drive
|
71 |
# if set, will generate a report via email or by storing on a drive
|
| 72 |
'report' => {
|
72 |
'report' => {
|
| 73 |
'email' => 'tech@example.org',
|
73 |
'email' => 'tech@example.org',
|
| Line 80... |
Line 80... |
| 80 |
'mount_point' => '',
|
80 |
'mount_point' => '',
|
| 81 |
}
|
81 |
}
|
| 82 |
}
|
82 |
}
|
| 83 |
},
|
83 |
},
|
| 84 |
#information about target server
|
84 |
#information about target server
|
| 85 |
'target_server' => {
|
85 |
'target' => {
|
| 86 |
'hostname' => '', # used to see if we are on target
|
86 |
'hostname' => '', # used to see if we are on target
|
| 87 |
'poolname' => 'backup', # name of the ZFS pool to import
|
87 |
'poolname' => 'backup', # name of the ZFS pool to import
|
| 88 |
# if this is set, the dataset uses GELI, so we must decrypt and
|
88 |
# if this is set, the dataset uses GELI, so we must decrypt and
|
| 89 |
# mount it first
|
89 |
# mount it first
|
| 90 |
'geli' => {
|
90 |
'geli' => {
|
| Line 113... |
Line 113... |
| 113 |
}
|
113 |
}
|
| 114 |
}
|
114 |
}
|
| 115 |
},
|
115 |
},
|
| 116 |
'transport' => {
|
116 |
'transport' => {
|
| 117 |
# this is the GPT label of the sneakernet disk
|
117 |
# this is the GPT label of the sneakernet disk
|
| 118 |
'disk_label' => 'sneakernet',
|
118 |
'label' => 'sneakernet',
|
| 119 |
# this is the file system type. Not needed if ufs
|
119 |
# this is the file system type. Not needed if ufs
|
| 120 |
'fstype' => 'ufs',
|
120 |
'fstype' => 'ufs',
|
| 121 |
# where we want to mount it
|
121 |
# where we want to mount it
|
| 122 |
'mount_point' => '/mnt/sneakernet',
|
122 |
'mount_point' => '/mnt/sneakernet',
|
| 123 |
# amount of time to wait for the disk to appear
|
123 |
# amount of time to wait for the disk to appear
|
| Line 259... |
Line 259... |
| 259 |
$logFileName = $config->{'log_file'} //= "$scriptFullPath.log";
|
259 |
$logFileName = $config->{'log_file'} //= "$scriptFullPath.log";
|
| 260 |
# log only for one run
|
260 |
# log only for one run
|
| 261 |
unlink ( $logFileName ) if -f $logFileName;
|
261 |
unlink ( $logFileName ) if -f $logFileName;
|
| 262 |
|
262 |
|
| 263 |
fatalError( "Invalid config file: missing source and/or target server" )
|
263 |
fatalError( "Invalid config file: missing source and/or target server" )
|
| 264 |
unless (defined $config->{source_server} && defined $config->{target_server});
|
264 |
unless (defined $config->{source} && defined $config->{target});
|
| 265 |
|
265 |
|
| 266 |
# mount the transport drive, fatal error if we can not find it
|
266 |
# mount the transport drive, fatal error if we can not find it
|
| 267 |
fatalError( "Unable to mount tranport drive with label $config->{transport}->{disk_label}" )
|
267 |
fatalError( "Unable to mount tranport drive with label $config->{transport}->{disk_label}" )
|
| 268 |
unless $config->{transport}->{mount_point} = mountDriveByLabel( $config->{transport} );
|
268 |
unless $config->{transport}->{mount_point} = mountDriveByLabel( $config->{transport} );
|
| 269 |
|
269 |
|
| 270 |
my $servername = `hostname -s`;
|
270 |
my $servername = `hostname -s`;
|
| 271 |
chomp $servername;
|
271 |
chomp $servername;
|
| 272 |
my $runningAs = $servername eq $config->{source_server}->{hostname} ? 'source' :
|
272 |
my $runningAs = $servername eq $config->{source}->{hostname} ? 'source' :
|
| 273 |
$servername eq $config->{target_server}->{hostname} ? 'target' : 'unknown';
|
273 |
$servername eq $config->{target}->{hostname} ? 'target' : 'unknown';
|
| - |
|
274 |
|
| 274 |
if ( $runningAs eq 'source' ) {
|
275 |
if ( $runningAs eq 'source' ) {
|
| 275 |
logMsg "Running as source server";
|
276 |
logMsg "Running as source server";
|
| 276 |
# remove all files from transport disk, but leave all subdirectories alone
|
277 |
# remove all files from transport disk, but leave all subdirectories alone
|
| 277 |
cleanDirectory( $config->{transport}->{mount_point} );
|
278 |
cleanDirectory( $config->{transport}->{mount_point} );
|
| 278 |
my $statusList = getStatusFile($config->{status_file});
|
279 |
my $statusList = getStatusFile($config->{status_file});
|
| 279 |
$statusList = doSourceReplication($config, $statusList);
|
280 |
$statusList = doSourceReplication($config, $statusList);
|
| 280 |
writeStatusFile($config->{status_file}, $statusList);
|
281 |
writeStatusFile($config->{status_file}, $statusList);
|
| 281 |
} elsif ( $runningAs eq 'target' ) {
|
282 |
} elsif ( $runningAs eq 'target' ) {
|
| 282 |
logMsg "Running as target server";
|
283 |
logMsg "Running as target server";
|
| 283 |
die "Target Server code not complete\n";
|
- |
|
| 284 |
die "GELI target server logic not yet implemented\n" if ( defined $config->{target_server}->{geli} );
|
- |
|
| 285 |
mountGeli( $config->{target_server}->{geli} ) if ( defined $config->{target_server}->{geli} );
|
284 |
mountGeli( $config->{target}->{geli} ) if ( defined $config->{target}->{geli} );
|
| 286 |
} else {
|
285 |
} else {
|
| 287 |
logMsg "This server ($servername) is neither source nor target server as per config\n";
|
286 |
fatalError( "This server ($servername) is neither source nor target server as per config\n" );
|
| 288 |
die;
|
- |
|
| 289 |
}
|
287 |
}
|
| 290 |
|
288 |
|
| 291 |
# add disk space utilization information on transport to the log
|
289 |
# add disk space utilization information on transport to the log
|
| 292 |
logMsg( "Disk space utilization on transport disk:\n" . runCmd( "df -h $config->{transport}->{mount_point}" ) . "\n" );
|
290 |
logMsg( "Disk space utilization on transport disk:\n" . runCmd( "df -h $config->{transport}->{mount_point}" ) . "\n" );
|
| 293 |
# add information about the server (zpools) to the log
|
291 |
# add information about the server (zpools) to the log
|
| Line 295... |
Line 293... |
| 295 |
|
293 |
|
| 296 |
# unmount the sneakernet drive
|
294 |
# unmount the sneakernet drive
|
| 297 |
`umount $config->{transport}->{mount_point}`;
|
295 |
`umount $config->{transport}->{mount_point}`;
|
| 298 |
# and remove the directory
|
296 |
# and remove the directory
|
| 299 |
rmdir $config->{transport}->{mount_point};
|
297 |
rmdir $config->{transport}->{mount_point};
|
| 300 |
sendReport( $config->{$runningAs}->{report}, "sneakernet replication completed on server $servername", $config->{log_file} );
|
- |
|
| 301 |
|
298 |
|
| 302 |
|
299 |
|
| 303 |
1;
|
300 |
1;
|
| 304 |
|
301 |
|
| 305 |
|
302 |
|